• 前端质量分析


    先上大王博客https://www.cnblogs.com/alex3714/articles/5714238.html,说的很有道理,我都快信了,所以一直想把这个项目写出来,怎奈大王一直不讲,唉,实在没办法只好自己写了。理念就是模仿大王的,实际操作流程有些不一样:

    1. 还是从前端js获取一些相关的前端性能指标
    2. 讲这些性能指标统一发送给我提供的接口
    3. 这个接口会对发送过来的数据进行一些处理
    4. 将处理的数据扔进kafka队列
    5. 从kafka队列里取出数据存进influxdb
    6. 从influxdb取出数据进行展示

    我的流程就是这样,其实都是很简单易懂的,但是这里面有一些小处理,大概有:

    1. js我是百度了大佬的,他的js可以取出前端性能相关的指标,可是取出来的加载时间他喵的都是负值…..实在是心情复杂,还好最后改好了,这是最坑我的地方,因为我前端不好
    2. 本来是想直接把数据存进influxdb,但是仔细想想还是应当先放入kafka,然后由需要的地方自己去取就是了,当然我现在只有向influxdb存,之后可以继续加,这样比较好拓展
    3. 基于第二点,我需要一个接收数据并扔进kafka的api(这个很随意)以及一个(或多个)从kafka里取数据并存储到对应后端的进程,这个进程是一直监听kafka队列运行的

    因为是放在业务前端里获取的数据,那么数据量肯定随着业务峰谷变化。再一个我原本想集成在我的django里,但是查了半天资料也没找到如何让django运行期间一直保持另外几个进程一直运行(本来是想用threading)。最后想想算了,直接用go写不就行了,性能好,开几个goroutine问题全都解决了,我只需要把前端展示集成下不就好了嘛。语言选好了,逻辑流程清晰了,那就开搞

    从前端开始,我直接把改好的js贴进来,大家复制就是了,唯一需要更改的是第179行改成你的api地址(也就是go提供的接口)

      1 (function(window) {
      2     'use strict';
      3     
      4     /**
      5      * https://developer.mozilla.org/zh-CN/docs/Web/API/Window/performance
      6      */
      7     var performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance || {};
      8     performance.now = (function() {
      9         return performance.now    ||
     10         performance.webkitNow     ||
     11         performance.msNow         ||
     12         performance.oNow          ||
     13         performance.mozNow        ||
     14         function() { return new Date().getTime(); };
     15     })();
     16     
     17     /**
     18      * 默认属性
     19      */
     20     var defaults = {
     21         performance: performance, // performance对象
     22         ajaxs: [], //ajax监控
     23         //可自定义的参数
     24         param: {
     25             // rate: 0.5, //随机采样率
     26             // src: 'http://127.0.0.1:8000/thief/a', //请求发送数据
     27             // download: {img:'http://h5dev.eclicks.cn/libs/common/img/bandwidth-5.png', size:4511798}//网速设置
     28         }
     29     };
     30     
     31     if(window.primus.param) {
     32         for(var key in window.primus.param) {
     33             defaults.param[key] = window.primus.param[key];
     34         }
     35     }
     36     var primus = defaults;
     37     var firstScreenHeight = window.innerHeight;//第一屏高度
     38     var doc = window.document;
     39     
     40     /**
     41      * 异常监控
     42      * https://github.com/BetterJS/badjs-report
     43      * @param {String}  msg   错误信息
     44      * @param {String}  url      出错文件的URL
     45      * @param {Long}    line     出错代码的行号
     46      * @param {Long}    col   出错代码的列号
     47      * @param {Object}  error       错误信息Object https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error
     48      */
     49     window.onerror = function(msg, url, line, col, error) {
     50         var newMsg = msg;
     51         if (error && error.stack) {
     52             var stack = error.stack.replace(/
    /gi, "").split(/at/).slice(0, 9).join("@").replace(/?[^:]+/gi, "");
     53             var msg = error.toString();
     54             if (stack.indexOf(msg) < 0) {
     55                 stack = msg + "@" + stack;
     56             }
     57             newMsg = stack;
     58         }
     59 //        if (Object.prototype.toString.call(newMsg) === "[object Event]") {
     60 //          newMsg += newMsg.type ? ("--" + newMsg.type + "--" + (newMsg.target ? (newMsg.target.tagName + "::" + newMsg.target.src) : "")) : "";
     61 //      }
     62         
     63         var obj = {msg:newMsg, target:url, rowNum:line, colNum:col};
     64         alert(obj.msg);
     65     };
     66     
     67     /**
     68      * ajax监控
     69      * https://github.com/HubSpot/pace
     70      */
     71     var _XMLHttpRequest = window.XMLHttpRequest;// 保存原生的XMLHttpRequest
     72     // 覆盖XMLHttpRequest
     73     window.XMLHttpRequest = function(flags) {
     74         var req;
     75         // 调用原生的XMLHttpRequest
     76         req = new _XMLHttpRequest(flags);
     77         // 埋入我们的“间谍”
     78         monitorXHR(req);
     79         return req;
     80     };
     81     var monitorXHR = function(req) {
     82         req.ajax = {};
     83         //var _change = req.onreadystatechange;
     84         req.addEventListener('readystatechange', function() {
     85             if(this.readyState == 4) {
     86                 req.ajax.end = primus.now();//埋点
     87 
     88                 if ((req.status >= 200 && req.status < 300) || req.status == 304 ) {//请求成功
     89                     req.ajax.endBytes = _kb(req.responseText.length * 2);//KB
     90                     //console.log('响应数据:'+ req.ajax.endBytes);//响应数据大小
     91                 }else {//请求失败
     92                     req.ajax.endBytes = 0;
     93                 }
     94                 req.ajax.interval = req.ajax.end - req.ajax.start;
     95                 primus.ajaxs.push(req.ajax);
     96                 //console.log('ajax响应时间:'+req.ajax.interval);
     97             }
     98         }, false);
     99         
    100         // “间谍”又对open方法埋入了间谍
    101         var _open = req.open;
    102         req.open = function(type, url, async) {
    103             req.ajax.type = type;//埋点
    104             req.ajax.url = url;//埋点
    105             return _open.apply(req, arguments);
    106         };
    107         
    108         var _send = req.send;
    109         req.send = function(data) {
    110             req.ajax.start = primus.now();//埋点
    111             var bytes = 0;//发送数据大小
    112             if(data) {
    113                 req.ajax.startBytes = _kb(JSON.stringify(data).length * 2 );
    114             }
    115             return _send.apply(req, arguments);
    116         };
    117     };
    118     
    119     /**
    120      * 计算KB值
    121      * http://stackoverflow.com/questions/1248302/javascript-object-size
    122      */
    123     function _kb(bytes) {
    124         return (bytes / 1024).toFixed(2);//四舍五入2位小数
    125     }
    126     
    127     /**
    128      * 给所有在首屏的图片绑定load事件,计算载入时间
    129      * TODO 忽略了异步加载
    130      * CSS背景图 是显示的在param参数中设置backgroundImages图片路径数组加载
    131      */
    132     var imgLoadTime = 0;
    133     function _setCurrent() {
    134         var current = Date.now();
    135         current > imgLoadTime && (imgLoadTime = current);
    136     }
    137     doc.addEventListener('DOMContentLoaded', function() {
    138         var imgs = doc.querySelectorAll('img');
    139         imgs = [].slice.call(doc.querySelectorAll('img'));
    140         if(imgs) {
    141             imgs.forEach(function(img) {
    142                 if(img.getBoundingClientRect().top > firstScreenHeight) {
    143                     return;
    144                 }
    145     //            var image = new Image();
    146     //          image.src = img.getAttribute('src');
    147                 if(img.complete) {
    148                     _setCurrent();
    149                 }
    150                 //绑定载入时间
    151                 img.addEventListener('load', function() {
    152                     _setCurrent();
    153                 }, false);
    154             });
    155         }
    156         
    157         //在CSS中设置了BackgroundImage背景
    158         if(primus.param.backgroundImages) {
    159             primus.param.backgroundImages.forEach(function(url) {
    160                 var image = new Image();
    161                 image.src = url;
    162                 if(image.complete) {
    163                     _setCurrent();
    164                 }
    165                 image.onload = function() {
    166                     _setCurrent();
    167                 };
    168             });
    169         }
    170     }, false);
    171 
    172     window.addEventListener('load', function() {
    173         //测试网速
    174         //_measureConnectionSpeed();
    175         setTimeout(function() {
    176             var time = primus.getTimes();
    177 
    178             $.ajax({
    179                 url: 'http://192.168.56.1:8080/',
    180                 type: 'POST',
    181                 dataType: 'json',
    182                 data: time,
    183                 success: function (data) {
    184 
    185                 }
    186             });
    187 
    188             //通过网页大小测试网速
    189 //            var duration = time.domReadyTime / 1000;
    190 //            var pageSize = doc.documentElement.innerHTML.length * 2 * 8;
    191 //            var speedBps = pageSize / duration;
    192 //            console.log(speedBps/(1024*1024));
    193 
    194             var data = {ajaxs:primus.ajaxs, dpi:primus.dpi(), time:time};
    195             primus.send(data);
    196         }, 500);
    197     });
    198 
    199     /**
    200      * 打印特性 key:value格式
    201      */
    202     primus.print = function(obj, left, right, filter) {
    203         var list = [], left = left || '', right = right || '';
    204         for(var key in obj) {
    205             if(filter) {
    206                 if(filter(obj[key]))
    207                     list.push(left + key + ':' + obj[key] + right);
    208             }else {
    209                 list.push(left + key + ':' + obj[key] + right);
    210             }
    211         }
    212         return list;
    213     };
    214     
    215     /**
    216      * 请求时间统计
    217      * 需在window.onload中调用
    218      * https://github.com/addyosmani/timing.js
    219      */
    220     primus.getTimes = function() {
    221         var timing = performance.timing;
    222         if (timing === undefined) {
    223             return false;
    224         }
    225         var api = {};
    226         //存在timing对象
    227         if (timing) {
    228             // All times are relative times to the start time within the
    229             // 白屏时间,也就是开始解析DOM耗时
    230             var firstPaint = 0;
    231 
    232             // Chrome
    233             if (window.chrome && window.chrome.loadTimes) {
    234                 // Convert to ms
    235                 firstPaint = window.chrome.loadTimes().firstPaintTime * 1000;
    236                 api.firstPaintTime = firstPaint;
    237             }
    238             // IE
    239             else if (typeof timing.msFirstPaint === 'number') {
    240                 firstPaint = timing.msFirstPaint;
    241                 api.firstPaintTime = firstPaint;
    242             }
    243             else {
    244                 api.firstPaintTime = timing.navigationStart;
    245             }
    246             // Firefox
    247             // This will use the first times after MozAfterPaint fires
    248             //else if (window.performance.timing.navigationStart && typeof InstallTrigger !== 'undefined') {
    249             //    api.firstPaint = window.performance.timing.navigationStart;
    250             //    api.firstPaintTime = mozFirstPaintTime - window.performance.timing.navigationStart;
    251             //}
    252 
    253             /**
    254              * http://javascript.ruanyifeng.com/bom/performance.html
    255              * 加载总时间
    256              * 这几乎代表了用户等待页面可用的时间
    257              * loadEventEnd(加载结束)-navigationStart(导航开始)
    258              */
    259             api.loadTime = timing.loadEventEnd - timing.navigationStart;
    260 
    261             /**
    262              * Unload事件耗时
    263              */
    264             api.unloadEventTime = timing.unloadEventEnd - timing.unloadEventStart;
    265 
    266             /**
    267              * 执行 onload 回调函数的时间
    268              * 是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
    269              */
    270             api.loadEventTime = timing.loadEventEnd - timing.loadEventStart;
    271 
    272             /**
    273              * 用户可操作时间
    274              */
    275             api.domReadyTime = timing.domContentLoadedEventEnd - timing.navigationStart;
    276 
    277             /**
    278              * 首屏时间
    279              * 用户在没有滚动时候看到的内容渲染完成并且可以交互的时间
    280              * 记录载入时间最长的图片
    281              */
    282             if(imgLoadTime == 0) {
    283                 api.firstScreen = api.domReadyTime;
    284             }else {
    285                 api.firstScreen = imgLoadTime - timing.navigationStart;
    286             }
    287 
    288             /**
    289              * 解析 DOM 树结构的时间
    290              * 期间要加载内嵌资源
    291              * 反省下你的 DOM 树嵌套是不是太多了
    292              */
    293             api.parseDomTime = timing.domComplete - timing.domInteractive;
    294 
    295             /**
    296              * 请求完毕至DOM加载耗时
    297              */
    298             api.initDomTreeTime = timing.domInteractive - timing.responseEnd;
    299 
    300             /**
    301              * 准备新页面时间耗时
    302              */
    303             api.readyStart = timing.fetchStart - timing.navigationStart;
    304 
    305             /**
    306              * 重定向的时间
    307              * 拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
    308              */
    309             api.redirectTime = timing.redirectEnd - timing.redirectStart;
    310 
    311             /**
    312              * DNS缓存耗时
    313              */
    314             api.appcacheTime = timing.domainLookupStart - timing.fetchStart;
    315 
    316             /**
    317              * DNS查询耗时
    318              * DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
    319              * 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)
    320              */
    321             api.lookupDomainTime = timing.domainLookupEnd - timing.domainLookupStart;
    322 
    323             /**
    324              * TCP连接耗时
    325              */
    326             api.connectTime = timing.connectEnd - timing.connectStart;
    327 
    328             /**
    329              * 内容加载完成的时间
    330              * 页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
    331              */
    332             api.requestTime = timing.responseEnd - timing.requestStart;
    333 
    334             /**
    335              * 请求文档
    336              * 开始请求文档到开始接收文档
    337              */
    338             api.requestDocumentTime = timing.responseStart - timing.requestStart;
    339 
    340             /**
    341              * 接收文档
    342              * 开始接收文档到文档接收完成
    343              */
    344             api.responseDocumentTime = timing.responseEnd - timing.responseStart;
    345 
    346             /**
    347              * 读取页面第一个字节的时间
    348              * 这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
    349              * TTFB 即 Time To First Byte 的意思
    350              * 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
    351              */
    352             api.TTFB = timing.responseStart - timing.navigationStart;
    353         }
    354         return api;
    355     };
    356     
    357     /**
    358      * 与performance中的不同,仅仅是做时间间隔记录
    359      * https://github.com/nicjansma/usertiming.js
    360      */
    361     var marks = {};
    362     primus.mark = function(markName) {
    363         var now = performance.now();
    364         marks[markName] = {
    365             startTime: Date.now(),
    366             start: now,
    367             duration: 0
    368         };
    369     };
    370     
    371     /**
    372      * 计算两个时间段之间的时间间隔
    373      */
    374     primus.measure = function(startName, endName) {
    375         var start = 0, end = 0;
    376         if(startName in marks) {
    377             start = marks[startName].start;
    378         }
    379         if(endName in marks) {
    380             end = marks[endName].start;
    381         }
    382         return {
    383             startTime: Date.now(),
    384             start: start,
    385             end: end,
    386             duration: (end - start)
    387         };
    388     };
    389     
    390     /**
    391      * 资源请求列表
    392      * Safrai以及很多移动浏览器不支持
    393      * https://github.com/nurun/performance-bookmarklet
    394      * http://nicj.net/resourcetiming-in-practice/
    395      */
    396     primus.getEntries = function() {
    397         if (performance.getEntries === undefined) {
    398             return false;
    399         }
    400         
    401         var entries = performance.getEntriesByType('resource');
    402         var statis = [];
    403         entries.forEach(function(t, index) {
    404             var isRequest = t.name.indexOf("http") === 0;console.log(t.name)
    405 //            if (isRequest) {
    406 //                urlFragments = t.name.match(/://(.[^/]+)([^?]*)??(.*)/);
    407 //                
    408 //                maybeFileName = t.name.split("/").pop();
    409 //                fileExtension = maybeFileName.substr((Math.max(0, maybeFileName.lastIndexOf(".")) || Infinity) + 1);
    410 //            } else {
    411 //                urlFragments = ["", window.location.host];
    412 //                fileExtension = t.name.split(":")[0];
    413 //            }
    414             var cur = {
    415                 name: t.name,
    416                 fileName: t.name.split("/").pop(),
    417                 //initiatorType: t.initiatorType || fileExtension || "SourceMap or Not Defined",
    418                 duration: t.duration
    419                 //isRequestToHost: urlFragments[1] === location.host
    420             };
    421 
    422             if (t.requestStart) {
    423                 cur.requestStartDelay = t.requestStart - t.startTime;
    424                 // DNS 查询时间
    425                 cur.lookupDomainTime = t.domainLookupEnd - t.domainLookupStart;
    426                 // TCP 建立连接完成握手的时间
    427                 cur.connectTime = t.connectEnd - t.connectStart;
    428                 // TTFB
    429                 cur.TTFB = t.responseStart - t.startTime;
    430                 // 内容加载完成的时间
    431                 cur.requestTime = t.responseEnd - t.requestStart;
    432                 // 请求区间
    433                 cur.requestDuration = t.responseStart - t.requestStart;
    434                 // 重定向的时间
    435                 cur.redirectTime = t.redirectEnd - t.redirectStart;
    436             }
    437             
    438             if (t.secureConnectionStart) {
    439                 cur.ssl = t.connectEnd - t.secureConnectionStart;
    440             }
    441             
    442             statis.push(cur);
    443         });
    444         return statis;
    445     };
    446     
    447     /**
    448      * 标记时间
    449      * Date.now() 会受系统程序执行阻塞的影响不同
    450      * performance.now() 的时间是以恒定速率递增的,不受系统时间的影响(系统时间可被人为或软件调整)
    451      */
    452     primus.now = function() {
    453         return performance.now();
    454     };
    455     
    456     /**
    457      * 网络状态
    458      * https://github.com/daniellmb/downlinkMax
    459      * http://stackoverflow.com/questions/5529718/how-to-detect-internet-speed-in-javascript
    460      */
    461     primus.network = function() {
    462         //2.2--4.3安卓机才可使用
    463         var connection = window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection;
    464         var types = "Unknown Ethernet WIFI 2G 3G 4G".split(" ");
    465         var network = {bandnull, type:null}
    466         if(connection && connection.type) {
    467             network.type = types[connection.type];
    468         }
    469         
    470         return network;
    471     };
    472     
    473     /**
    474      * 测试网速
    475      */
    476     function _measureConnectionSpeed() {
    477         var startTime, endTime;
    478         var download = new Image();
    479         download.onload = function () {
    480             endTime = primus.now();
    481             var duration = (endTime - startTime) / 1000;
    482             var bitsLoaded = downloadSize * 8;
    483             var speedBps = (bitsLoaded / duration).toFixed(2);
    484             var speedKbps = (speedBps / 1024).toFixed(2);
    485             var speedMbps = (speedKbps / 1024).toFixed(2);
    486             console.log(speedMbps);
    487         }
    488         startTime = primus.now();
    489         var cacheBuster = "?rand=" + startTime;
    490         download.src = imageAddr + cacheBuster;
    491     }
    492     
    493     /**
    494      * 代理信息
    495      */
    496     primus.ua = function() {
    497         return USERAGENT.analyze(navigator.userAgent);
    498 //        var parser = new UAParser();
    499 //        return parser.getResult();
    500     };
    501     
    502     /**
    503      * 分辨率
    504      */
    505     primus.dpi = function() {
    506         return {window.screen.width, height:window.screen.height};
    507     };
    508     
    509     /**
    510      * 组装变量
    511      * https://github.com/appsignal/appsignal-frontend-monitoring
    512      */
    513     function _paramify(obj) {
    514         return 'data=' + JSON.stringify(obj);
    515     }
    516     
    517     /**
    518      * 推送统计信息
    519      */
    520     primus.send = function(data) {
    521         var ts = new Date().getTime().toString();
    522         //采集率
    523         if(primus.param.rate > Math.random(0, 1)) {
    524             var img = new Image(0, 0);
    525             img.src = primus.param.src +"?" + _paramify(data) + "&ts=" + ts;
    526         }
    527     };
    528     
    529     var currentTime = Date.now(); //这个脚本执行完后的时间 计算白屏时间
    530     window.primus = primus;
    531 })(this);
    View Code

    然后是你要检测的前端页面,直接把该js引用就ok了

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <script src="/statics/js/jquery-1.10.1.min.js" type="text/javascript"></script>
    
        <script type='text/javascript'>
            window.primus || (primus={});
        </script>
        <script src="/statics/js/primus.js"></script>
    </head>
    <body>
    
        <div>Index</div>
    
    </body>
    </html>

    这样,每当有人访问该页面时,就会取出他此次访问页面相关质量然后post到我们的api上

    然后是api进行处理,代码在git上:https://github.com/bfmq/Hermes

    首先是前端质量的基本字段,然后再加上这个页面的url(因为你肯定不止检测一个页面嘛,之后前端展示的时候根据url进行不同的查询语句就可以了),最后根据访问ip调阿里的api获取了这个ip所属的地区,这样就可以知道地区进行一些判断了。在程序开始运行的时候就会打开一个一直从kafka队列取数据并录入influxdb的goroutine,你可以再加一些自己的插件进去,录入到大数据里一类的。当然conf下的配置你得改成你自己的服务器的。

    后台内部处理完成了,就要在前端展示了,这个还是使用了python继承在django里了,前端出图用的还是echarts,数据就是从influxdb里取得(这里肯定又是python的api),数据还是那个数据,具体想用什么图展示就看你开心了,我就简单的直接全部展示了下,设置的是每一分钟会自动ajax再去取后刷新下xy轴(还是echarts里的功能)

    当然了,现在展示的数据只是我测试页面的测试数据,所以都是几ms级别的,检测的页面几乎没写内容嘛毕竟,但是经过使用,还是ok的

    最后再附上各名词对应关系跟我python获取数据的代码

    "firstPaint""白屏时间"
    
    "loadTime""加载总时间"
    
    "unloadEventTime""Unload事件耗时"
    
    "loadEventTime""onload"回调函数时间"
    
    "domReadyTime""用户可操作时间"
    
    "firstScreen""首屏时间"
    
    "parseDomTime""DOM树结构解析时间"
    
    "initDomTreeTime""请求完毕至DOM加载耗时"
    
    "readyStart""准备新页面时间耗时"
    
    "redirectTime""重定向的时间"
    
    "appcacheTime""DNS缓存耗时"
    
    "lookupDomainTime""DNS查询耗时"
    
    "connectTime""TCP连接耗时"
    
    "requestTime""内容加载完成的时间"
    
    "requestDocumentTime""请求文档时间"
    
    "responseDocumentTime""接收文档时间"
    
    "TTFB""读取页面第一个字节的时间"
    def get_influxdb_data(url, city):
        """
        从hermes库里获取数据
        :param url:  索引,你要查看的url
        :param city:  表名,你要查看的城市
        :return: 
        """
        data = {}
        query = """select  TTFB,appcacheTime,connectTime,domReadyTime,firstScreen,initDomTreeTime,loadEventTime,
        loadTime,lookupDomainTime,parseDomTime,readyStart,redirectTime,requestDocumentTime,requestTime,responseDocumentTime,
        unloadEventTime from "{0}" where url = '{1}' and time > now() - 1h;""".format(city, url)
        influxdb_obj = InfluxDBCFactory('hermes')
        query_ret = influxdb_obj.query(query)
        all_data = query_ret.raw['series'][0]
        all_data_columns = all_data['columns']
        all_data_values = all_data['values']
    
        for key in all_data_columns:
            key_index = all_data_columns.index(key)
            if key != 'time':
                key_list = [x[key_index] for x in all_data_values]
            else:
                key_list = [utc2local(x[key_index], local_format='%H:%M:%S') for x in all_data_values]
            data[FrontendData[key]] = key_list
        return data
    #!/usr/bin/env python
    # -*- coding:utf8 -*-
    # __author__ = '北方姆Q'
    
    from influxdb import InfluxDBClient
    from plugins.duia.singleton import Singleton
    from django.conf import settings
    
    
    class InfluxDBCFactory(InfluxDBClient, Singleton):
        def __init__(self, database, host=settings.INFLUXDB_SERVER, port=settings.INFLUXDB_PORT):
            super().__init__(host=host, port=port, database=database)
    #!/usr/bin/env python
    import time
    import datetime
    
    # 格式自改
    UTC_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
    LOCAL_FORMAT = '%Y-%m-%d %H:%M:%S'
    
    
    def utc2local(utc_str, utc_format=UTC_FORMAT, local_format=LOCAL_FORMAT):
        utc_st = datetime.datetime.strptime(utc_str, utc_format)
        local_time = datetime.datetime.fromtimestamp(time.time())
        utc_time = datetime.datetime.utcfromtimestamp(time.time())
        time_difference = local_time - utc_time
        local_st = utc_st + time_difference
        return local_st.strftime(local_format)
  • 相关阅读:
    201771010108-韩腊梅 实验三 结对项目—《西北师范大学疫情防控信息系统》项目报告
    201771010108-韩腊梅 实验二 个人项目—《西北师范大学学生疫情上报系统》项目报告
    201771010108-韩腊梅 实验一 软件工程准备—<对软件工程的初步了解>
    201771010108 -韩腊梅-第十八周学习总结
    201771010108 -韩腊梅-第十七周学习总结
    201771010108 -韩腊梅-第十六周学习总结
    201771010108 -韩腊梅-第十五周学习总结
    2020软件工程-获得小黄衣有感
    201771030118-司绍斌 实验四 软件项目案例分析
    201771030118-司绍斌 实验三 结对项目—《西北师范大学疫情防控信息系统》项目报告
  • 原文地址:https://www.cnblogs.com/bfmq/p/9467820.html
Copyright © 2020-2023  润新知