日志管理(6) 写一个超级简单的日志查询页面

把所有的业务日志通过scribe收集上来后,需要给项目开发人员一个查询业务日志的页面,方便他们查日志也给我们运维节约时间,我们可以用节约的时间做更多更有意义的事。写的这个页面很简单后端用的是Django,前端展示用的是基于bootstrap的Metronic模板,因为不需要权限控制也不要登录验证故没有用到数据库。用一个业务日志查询页面来介绍下,其它业务日志查询的逻辑都一样甚至渲染的模板都几乎一样,唯一不同的是渲染模板传递的参数有小差异。实现的功能很简单:可以查询每天最新的日志如最新100行;根据日期查询某天日志最后100行;根据一个或多个关键字查询日志并显示最新的200行结果;每次查询的结果都可以通过页面的下载按钮下载到本地供日后分析;页面上显示的结果按时间排序,最新的显示在最前面。1、渲染模板,每个业务日志模板都由一个基础模板和各自独有的模板构成。基础模板bash.html

<!DOCTYPE html><!--[if IE 8]> <html lang="en" class="ie8"> <![endif]--><!--[if IE 9]> <html lang="en" class="ie9"> <![endif]--><!--[if !IE]><!--> <html lang="en"> <!--<![endif]--><!-- BEGIN HEAD --><head><meta charset="utf-8" />    <title>{{privatetitle}}</title><meta content="width=device-width, initial-scale=1.0" name="viewport" /><meta content="" name="description" /><meta content="" name="author" /><!-- BEGIN GLOBAL MANDATORY STYLES --><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/bootstrap.min.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/font-awesome.min.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/style-metro.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/style.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/style-responsive.css" rel="stylesheet" type="text/css"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/default.css" rel="stylesheet" type="text/css" id="style_color"/><link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/uniform.default.css" rel="stylesheet" type="text/css"/><!-- END GLOBAL MANDATORY STYLES -->    <!-- BEGIN PAGE LEVEL STYLES -->    <link rel="stylesheet" type="text/css" href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/datepicker.css" />    <link href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/css/glyphicons.css" rel="stylesheet" /><!-- END PAGE LEVEL STYLES --><link rel="shortcut icon" href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/image/favicon.ico" /></head><!-- END HEAD --><!-- BEGIN BODY --><body class="page-header-fixed"><!-- BEGIN HEADER --><div class="header navbar navbar-inverse navbar-fixed-top"><!-- BEGIN TOP NAVIGATION BAR --><div class="navbar-inner"><div class="container-fluid"><!-- BEGIN LOGO --><a class="brand" href="http://www.xiaomastack.com/seek/"><img src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/image/logo.png" alt="logo" /></a><!-- END LOGO --><!-- BEGIN RESPONSIVE MENU TOGGLER --><a href="javascript:;" class="btn-navbar collapsed" data-toggle="collapse" data-target=".nav-collapse"><img src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/image/menu-toggler.png" alt="" /></a>          <!-- END RESPONSIVE MENU TOGGLER --><!-- BEGIN TOP NAVIGATION MENU --><ul class="nav pull-right"><!-- BEGIN USER LOGIN DROPDOWN --><li class="dropdown user"><a href="http://www.xiaomastack.com/2014/11/16/logs-terrace/" class="dropdown-toggle" data-toggle="dropdown"><img alt="" src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/image/linux.jpg" /><span class="username">SomeBody</span><i class="icon-angle-down"></i></a><ul class="dropdown-menu"><li><a href="http://www.xiaomastack.com/2014/11/16/logs-terrace/"><i class="icon-tasks"></i> My Home</a></li><li class="divider"></li><li><a href="http://www.xiaomastack.com/2014/11/16/logs-terrace/"><i class="icon-key"></i> Log Out</a></li></ul></li><!-- END USER LOGIN DROPDOWN --></ul><!-- END TOP NAVIGATION MENU --></div></div><!-- END TOP NAVIGATION BAR --></div><!-- END HEADER --><!-- BEGIN CONTAINER --><div class="page-container row-fluid"><!-- BEGIN SIDEBAR --><div class="page-sidebar nav-collapse collapse"><!-- BEGIN SIDEBAR MENU -->        <ul class="page-sidebar-menu"><li><!-- BEGIN SIDEBAR TOGGLER BUTTON --><div class="sidebar-toggler hidden-phone"></div><!-- BEGIN SIDEBAR TOGGLER BUTTON --></li><li class="active ">                <a href="javascript:;"><i class="icon-table"></i><span class="title">日志查询</span><span class="selected"></span><span class="arrow open"></span>                </a><ul class="sub-menu">                        <li class={{seek01}}>                        <a href="http://www.xiaomastack.com/seek/aaaaa/">A日志</a></li>                        <li class={{seek02}}>                        <a href="http://www.xiaomastack.com/seek/bbbbb/nginx-php/">B日志</a></li>                        <li class={{seek03}}>                        <a href="http://www.xiaomastack.com/seek/ccccc/">C日志</a></li>                        <li class={{seek04}}>                        <a href="http://www.xiaomastack.com/seek/ddddd/services/">D日志</a></li>                        <li class={{seek05}}>                        <a href="http://www.xiaomastack.com/seek/eeeee/nginx-php/">E日志</a></li></ul></li></ul><!-- END SIDEBAR MENU --></div><!-- END SIDEBAR --><!-- BEGIN PAGE --><div class="page-content"><!-- BEGIN SAMPLE PORTLET CONFIGURATION MODAL FORM--><div id="portlet-config" class="modal hide"><div class="modal-header"><button data-dismiss="modal" class="close" type="button"></button><h3>portlet Settings</h3></div><div class="modal-body"><p>Here will be a configuration form</p></div></div><!-- END SAMPLE PORTLET CONFIGURATION MODAL FORM--><!-- BEGIN PAGE CONTAINER--><div class="container-fluid"><!-- BEGIN PAGE HEADER--><div class="row-fluid">                    <div class="span12">                        {% block rightareadown %}<br><h3>请选择项目</h3>{% endblock %}</div></div><!-- END PAGE CONTENT--></div><!-- END PAGE CONTAINER--></div><!-- END PAGE --> </div><!-- END CONTAINER --><!-- BEGIN FOOTER --><div class="footer"><div class="footer-inner">Copyright (c) 2014 日志查询平台. All rights reserved</div><div class="footer-tools"><span class="go-top"><i class="icon-angle-up"></i></span></div></div><!-- END FOOTER --><!-- BEGIN JAVASCRIPTS(Load javascripts at bottom, this will reduce page load time) --><!-- BEGIN CORE PLUGINS --><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery-1.10.1.min.js" type="text/javascript"></script><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery-migrate-1.2.1.min.js" type="text/javascript"></script><!-- IMPORTANT! Load jquery-ui-1.10.1.custom.min.js before bootstrap.min.js to fix bootstrap tooltip conflict with jquery ui tooltip --><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery-ui-1.10.1.custom.min.js" type="text/javascript"></script>      <script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/bootstrap.min.js" type="text/javascript"></script><!--[if lt IE 9]><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/excanvas.min.js"></script><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/respond.min.js"></script>  <![endif]-->   <script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery.slimscroll.min.js" type="text/javascript"></script><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery.blockui.min.js" type="text/javascript"></script>  <script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery.cookie.min.js" type="text/javascript"></script><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/jquery.uniform.min.js" type="text/javascript" ></script><!-- END CORE PLUGINS -->    <!-- BEGIN PAGE LEVEL PLUGINS -->    <script type="text/javascript" src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/bootstrap-datepicker.js"></script>    <script type="text/javascript" src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/date.js"></script>    <!-- END PAGE LEVEL PLUGINS --><!-- BEGIN PAGE LEVEL SCRIPTS --><script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/app.js"></script>    <script src="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{STATIC_URL}}/js/form-components.js"></script><script>jQuery(document).ready(function() {// initiate layout and plugins        App.init();        FormComponents.init();});</script><!-- END PAGE LEVEL SCRIPTS --><script type="text/javascript">  var _gaq = _gaq || [];  _gaq.push(['_setAccount', 'UA-37564768-1']);  _gaq.push(['_setDomainName', 'keenthemes.com']);  _gaq.push(['_setAllowLinker', true]);  _gaq.push(['_trackPageview']);  (function() {    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;    ga.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'stats.g.doubleclick.net/dc.js';    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);  })();</script></body><!-- END BODY --></html>

每个项目还有自己独有的模板且继承bash.html,独立模板用来重写{% block rightareadown %}{% endblock %}块,独立的模板几乎一样就举一例。

{% extends "base.html" %}{% block rightareadown %}<ul class="breadcrumb">    <li>        <i class="icon-home"></i>        <a href="http://www.xiaomastack.com/seek/">日志查询</a>     <i class="icon-angle-right"></i></li><li><a href="http://www.xiaomastack.com/seek/aaaaa/">A日志</a></li></ul><div class="row-fluid">    <div class="span12">        <!-- BEGIN PORTLET-->        <div class="portlet box grey">            <div class="portlet-title">                <div class="caption">                    <i class="icon-reorder"></i>填入必要的查询条件                </div>                <div class="tools">                    <a href="javascript:;" class="collapse"></a>                    <a href="javascript:;" class="remove"></a>                </div>            </div>        <div class="portlet-body form">        <!-- BEGIN FORM-->        <form action="/seek/aaaaa/" class="form-horizontal" enctype="multipart/form-data" method="post">            <div class="control-group">                <label class="control-label">选择查询日期(默认当天)</label>                <div class="controls">                    <div class="input-append date date-picker" data-date-format="mm/dd/yyyy" data-date-viewmode="years">                        <input class="m-wrap m-ctrl-medium date-picker" readonly size="16" type="text" value="" name="date"/><span class="add-on"><i class="icon-calendar"></i></span>                    </div>                </div>            </div>            <div class="control-group">                <label class="control-label">选择要查询的日志</label>                <div class="controls">                    <select class="span6 m-wrap" multiple="multiple" data-placeholder="Choose a Category" tabindex="1" name="logdir" >                        {% for dir in dirs %}                        <option>{{ dir }}</option>                        {% endfor %}                    </select>                </div>            </div>            <div class="control-group">                <label class="control-label">Keys</label>                <div class="controls">                    <input type="text" class="span6 m-wrap" name="keys" placeholder="输入关键字,多个关键字用分号分隔(不输入关键字则获取当天最新的100行记录)">                </div>            </div>            <div class="form-actions">                <button type="submit" class="btn blue">Submit</button>            </div>            {% csrf_token %}        </form>        <!-- END FORM-->          </div>    </div>    <!-- END PORTLET--></div><div class="row-fluid">    <div class="portlet box grey">        <div class="portlet-title">            <div class="caption">                <i class="icon-reorder"></i>查询结果            </div>            <div class="tools">                <a href="javascript:;" class="collapse"></a>            </div>        </div>    <div class="portlet-body">        <div class="control-group">            <a href="http://www.xiaomastack.com/2014/11/16/logs-terrace/{{durl}}" class="glyphicons no-js download_alt"><i></i>下载</a>        </div>    <div class="scroller" data-height="550px" data-always-visible="1">        {{ value }}        {% for mes in message %}        {{ mes }}<br><br>        {% endfor %}    </div>    </div>    </div></div>{% endblock %}

2、视图函数,接收前端页面的POST请求并根据不同的请求条件,使用日志查询函数searchKey, noKeys, readLine查询日志,然后将结果渲染后返回给前端页面。

#coding=utf-8from django.shortcuts import render_to_responsefrom django.template import RequestContextfrom os import listdir, pathfrom search import searchKey, noKeys, readLineimport timedef seekindex(request):    avg = {'privatetitle': '日志查询', 'STATIC_URL': '/static'}    return render_to_response('base.html', avg)def seekvenus(request):    message = ''    durl = '#'    value = ''    logPath = r"/var/scribe_data/aaaaa"    logList = listdir(logPath)    dirs = []    for dir in logList:        dirs.append(dir.replace("1_", ''))    dirs.sort()    if request.method == 'POST':        try:            date = request.POST['date']        except:            pass        try:            logdir = request.POST['logdir']        except:            logdir = 'nodir'        try:            keys = request.POST['keys']        except:            pass        if not date:            date = time.strftime('%m/%d/%Y',time.localtime(time.time()))         if date and logdir and keys:            #fname            temp = date.split('/')            dd = temp[1]             mm = temp[0]             yy = temp[2]            fname = '/var/scribe_data/aaaaa/'+'1_'+logdir+'/'+'1_'+logdir+'-'+yy+'-'+mm+'-'+dd+'_00000'            #keys            keys = keys.split(';')            #seek            if not path.exists(fname):                value = "当天的日志文件不存在"            else:                dfile = searchKey(fname, keys)                durl = r'/static/tmp/' + dfile.split('/')[-1]                message = readLine(dfile, False)        elif logdir == 'nodir':            value = '请选择日志'        else:            temp = date.split('/')            dd = temp[1]             mm = temp[0]             yy = temp[2]            fname = '/var/scribe_data/aaaaa/'+'1_'+logdir+'/'+'1_'+logdir+'-'+yy+'-'+mm+'-'+dd+'_00000'            if not path.exists(fname):                value = "当天的日志文件不存在"            else:                dfile = noKeys(fname)                 durl = r'/static/tmp/' + dfile.split('/')[-1]                message = readLine(dfile, True)    avg = {'privatetitle': 'aaaaa日志查询', 'STATIC_URL': '/static', 'message':message, 'seek01':"active" ,'dirs':dirs, 'durl':durl, 'value':value}    return render_to_response('aaaaa/seek.html', avg, context_instance=RequestContext(request))

3、日志查询函数。每次先将一定条件的查询结果保存到一个新的文件,然后再从查询结果中读取最新的100行显示,这个新的文件可以通过页面的下载链接上下载到本地。

#!/usr/bin/pythonfrom re import search, subimport loggingimport commands def searchKey(fname, keys, user='Unknown'):    logging.basicConfig(filename = '/var/www/logseek/log/seek.log', level = logging.INFO, filemode = 'a', format = '%(asctime)s - %(levelname)s: %(message)s')    tmpdir = r'/var/www/logseek/static/tmp/'     resultFile = tmpdir + fname.split(r'/')[-1]+'-'+keys[0]+'-'+keys[-1]+'.log'    file = open(fname)    refile = open(resultFile, 'w')    while True:        lines = file.readlines(500000)        if not lines:            break        for line in lines:            for key in keys:                row = search(key, str(line))                if not row:                    row = False                    break            if row:                refile.write(line)    file.close()    refile.close()    keyinfo = ''    for key in keys:        keyinfo = keyinfo + ':' + key    logging.info(user + ':' + fname + keyinfo)    return resultFiledef noKeys(fname, user='Unknown'):    logging.basicConfig(filename = '/var/www/logseek/log/seek.log', level = logging.INFO, filemode = 'a', format = '%(asctime)s - %(levelname)s: %(message)s')    tmpdir = r'/var/www/logseek/static/tmp/'    resultFile = tmpdir + fname.split(r'/')[-1]+'-'+'new100.log'    comand = 'tail -n 100 '+fname+' > '+ resultFile     try:        stat, proStr = commands.getstatusoutput(comand)    except:        pass    logging.info(user + ':' + fname + ' tail 100 lines')    return resultFiledef readLine(fname, flag):    if flag:        comand = 'tail -n 100 '+fname    else:        comand = 'tail -n 200 '+fname    try:        stat, proStr = commands.getstatusoutput(comand)    except:        pass    relist = proStr.split('\n')    relist.reverse()    return relist

4、最后看到的查询日志页面一个非常简单实用的日志查询页面,如果以后还有更多需求再慢慢堆代码。文章出处:http://www.xiaomastack.com/2014/11/16/logs-terrace/

就会犯错误,就会有无数次让自己跌倒的机会出现,

日志管理(6) 写一个超级简单的日志查询页面

相关文章:

你感兴趣的文章:

标签云: