Authored by 姜枫

add log query

/**
*
*
* @
*/
... ... @@ -17,6 +17,7 @@ import Socket from 'socket.io';
import config from './config/config';
import webApp from './apps/web';
import ws from './lib/ws';
import errorHandle from './middleware/error-handle';
const port = process.env.PORT || config.port;
... ... @@ -27,6 +28,9 @@ const io = new Socket(server);
ws.init(io);
app.keys = ['yoho-node-ci secret'];
app.use(errorHandle(app));
app.use(serve(__dirname + '/public'));
app.use(convert(session({}, app)));
app.use(convert(body({
... ... @@ -37,6 +41,24 @@ app.use(convert(body({
})));
app.use(mount('/', webApp));
// app.on('error', function(err, ctx) {
// console.log(err);
// switch (ctx.accepts('json', 'html', 'text')) {
// case 'json':
// ctx.body = {
// code: 400
// };
//
// break;
// case 'html':
// console.log(ctx);
// ctx.redirect('/500');
// break;
// default:
// break;
// }
// });
server.listen(port, () => {
console.log(`app started in ${port}`);
});
\ No newline at end of file
... ...
... ... @@ -15,18 +15,10 @@ let client = influx({
database: 'udp'
});
client.getSeriesNames((err, dbs) => {
console.log(err);
console.log(JSON.stringify(dbs));
});
client.query('select * from test_point limit 10', function(err, results) {
console.log(results);
});
const db = {
client: client,
query: (query) => {
console.log(query);
return new Promise((resolve, reject) => {
client.query(query, (err, result) => {
if (err) {
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/3
*/
'use strict'
import Router from 'koa-router';
let r = new Router();
const CommonPage = {
page_404: async function(ctx) {
await ctx.render('action/404');
},
page_500: async function(ctx) {
await ctx.render('action/500', {error: ctx.local.error});
}
};
r.get('/404', CommonPage.page_404);
r.get('/500', CommonPage.page_500);
export default r;
\ No newline at end of file
... ...
... ... @@ -7,7 +7,7 @@ let r = new Router();
const index = {
index: async(ctx, next) => {
ctx.redirect('/projects');
},
}
};
r.get('/', index.index);
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/4
*/
'use strict';
import Router from 'koa-router';
import {
Project
} from '../../models';
import InfluxDB from '../../logger/influxdb';
const r = new Router();
const monitor = {
async log(ctx) {
let id = ctx.query.pid;
let ip = ctx.query.ip;
let env = ctx.query.env;
let projects = await Project.findAll();
let data = {
projects: projects, ip: ip, env: [{
name: '线上环境',
value: 'production',
checked: env === 'production'
}, {
name: '测试环境',
value: 'test',
checked: env === 'test'
}]
};
if (id) {
projects.forEach((p) => {
p.checked = p._id === id;
if (env) {
data.hosts = p.deploy[env].target;
}
});
}
await ctx.render('action/log_view', data);
},
async query(ctx) {
let influxName = ctx.query.influxName;
let ip = ctx.query.ip;
let limit = ctx.query.limit || 30;
let page = ctx.query.page || 1;
let oldTime = ctx.query.oldTime;
let newTime = ctx.query.newTime;
ip = ip.replace(/\./g, '-');
let where = 'where host =~ /./ ';
if (ip) {
where += ` and host='ip-${ip}'`;
}
if (oldTime) {
where += ` and time<'${oldTime}' order by time desc`;
}
if (newTime) {
where += ` and time>'${newTime}'`;
}
if (!(oldTime || newTime)) {
where += ' order by time desc';
}
let sql = `select * from ${influxName} ${where} limit ${limit} OFFSET ${(page -1) * limit}`;
let logs = await InfluxDB.query(sql);
ctx.body = logs[0];
}
};
r.get('/log', monitor.log);
r.get('/log/query', monitor.query);
export default r;
\ No newline at end of file
... ...
... ... @@ -5,7 +5,6 @@ import moment from 'moment';
import Build from '../../ci/build';
import Deploy from '../../ci/deploy';
import ws from '../../../lib/ws';
import {
Building,
... ... @@ -26,7 +25,7 @@ const p = {
/**
* 所有项目首页
*/
index_page: async (ctx, next) => {
index_page: async(ctx, next) => {
let projects = await Project.findAll();
projects.forEach((p, i) => {
p.color = colors[i % colors.length];
... ... @@ -70,7 +69,7 @@ const p = {
});
},
new_page: async (ctx, next) => {
new_page: async(ctx, next) => {
let serversAll = await Server.findAll();
let servers = {};
serversAll.forEach(s => {
... ... @@ -90,7 +89,7 @@ const p = {
}
});
},
edit_page: async (ctx, next) => {
edit_page: async(ctx, next) => {
let id = ctx.query.id;
let project = await Project.findById(id);
... ... @@ -112,7 +111,7 @@ const p = {
servers: servers
});
},
save: async (ctx, next) => {
save: async(ctx, next) => {
let project = ctx.request.body;
let id = project._id;
delete project._id;
... ... @@ -120,15 +119,15 @@ const p = {
await Project.update({
_id: id
}, {
$set: project
});
$set: project
});
} else {
await Project.insert(project);
}
ctx.redirect('/projects');
ctx.status = 301;
},
buildings_table: async (ctx, next) => {
buildings_table: async(ctx, next) => {
let env = ctx.request.query.env;
let pid = ctx.params.id;
let buildings = await Building.cfind({
... ... @@ -144,7 +143,7 @@ const p = {
data: buildings
};
},
project_build: async (ctx, next) => {
project_build: async(ctx, next) => {
let pid = ctx.params.pid;
let env = ctx.request.body.env;
let branch = ctx.request.body.branch;
... ... @@ -176,7 +175,7 @@ const p = {
};
await next();
},
project_deploy: async (ctx) => {
project_deploy: async(ctx) => {
let buildingId = ctx.params.building;
let building = await Building.findById(buildingId);
if (!building) {
... ... @@ -188,7 +187,7 @@ const p = {
let project = await Project.findByName(building.project);
let targets = project.deploy[building.env].target;
targets.forEach(async (host) => {
targets.forEach(async(host) => {
let info = {
projectId: project._id,
host: host,
... ...
... ... @@ -6,7 +6,6 @@
import path from 'path';
import Koa from 'koa';
import convert from 'koa-convert';
import hbs from '../../middleware/yoho-koa-hbs';
import helpers from '../../lib/helpers';
... ... @@ -38,4 +37,6 @@ app.use(async(ctx, next) => {
});
routers(app);
export default app;
\ No newline at end of file
... ...
import Router from 'koa-router';
import common from './actions/common';
import index from './actions/index';
import projects from './actions/projects';
import servers from './actions/servers';
import login from './actions/login';
import monitor from './actions/monitor';
const noAuth = new Router();
const base = new Router();
... ... @@ -11,6 +13,8 @@ const base = new Router();
export default function (app) {
noAuth.use('', login.routes(), login.allowedMethods());
noAuth.use('', common.routes(), common.allowedMethods());
app.use(noAuth.routes(), noAuth.allowedMethods());
app.use(async (ctx, next) => {
... ... @@ -23,6 +27,7 @@ export default function (app) {
base.use('/projects', projects.routes(), projects.allowedMethods());
base.use('/servers', servers.routes(), servers.allowedMethods());
base.use('/monitor', monitor.routes(), monitor.allowedMethods());
base.use('', index.routes(), index.allowedMethods());
... ...
<section>
<div class="notfoundpanel" style="margin: 0 auto;">
<h1>404!</h1>
<h3>The page you are looking for has not been found!</h3>
<p>The page you are looking for might have been removed, had its name changed, or unavailable. Maybe you could try a search:</p>
</div><!-- notfoundpanel -->
</section>
\ No newline at end of file
... ...
<section>
<div class="notfoundpanel" style="margin: 0 auto;">
<h1>500!</h1>
<h3>The page you are looking for has not been found!</h3>
<p>The page you are looking for might have been removed, had its name changed, or unavailable. Maybe you could try a search:</p>
</div><!-- notfoundpanel -->
{{#error}}
<pre class="error" style="margin: 40px;">
{{this}}
</pre>
{{/error}}
</section>
\ No newline at end of file
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li>监控中心</li>
</ul>
<h4>运行日志</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<div class="contentpanel">
<div class="row">
<div class="col-sm-4 col-md-3">
<h4 class="md-title mb5">项目列表</h4>
<select id="projects" data-placeholder="Choose a Project..." class="width100p">
<option value="">Choose One</option>
{{#each projects}}
<option value="{{_id}}" {{#if checked}}selected{{/if}}>{{name}}</option>
{{/each}}
</select>
<div class="mb20"></div>
<h4 class="md-title mb5">运行环境</h4>
{{#each env}}
<div class="radio"><label><input type="radio" {{#if checked}}checked=""{{/if}} value="{{value}}"
name="env"> {{name}}</label></div>
{{/each}}
<div class="mb20"></div>
<h4 class="md-title mb5">Host列表</h4>
<select id="hosts" data-placeholder="Choose a Host..." class="width100p">
<option value="">Choose One</option>
{{#each hosts}}
<option value="{{this}}" {{#equals this @root.ip}}selected{{/equals}}>{{this}}</option>
{{/each}}
</select>
<div class="mb20"></div>
<button class="btn btn-success" id="load-btn">加载</button>
<br>
</div><!-- col-sm-4 -->
<div class="col-sm-8 col-md-9">
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">日志</h4>
<p>可通过滚动鼠标加载</p>
</div><!-- panel-heading -->
<div class="panel-body yoho-log-dark">
<div class="results-list ">
</div><!-- results-list -->
</div><!-- panel-body -->
</div><!-- panel -->
</div><!-- col-sm-8 -->
</div><!-- row -->
</div>
<script>
$(function() {
$('#projects').select2();
$('#hosts').select2();
var pid = $('#projects').val();
var env = $('input[name="env"]:checked').val();
var projects = {{{json projects}}};
var $logs = $('.yoho-log-dark .results-list');
var $logWrap = $('.yoho-log-dark');
var oldTime, newTime;
var influxName, ip;
var loading = false;
function changeIp(pid, env) {
if (pid && env) {
for (var i = 0; i < projects.length; i++) {
var p = projects[i];
if (p._id === pid) {
var hosts = p.deploy[env].target;
$('#hosts').select2("destroy");
$('#hosts').empty();
$('#hosts').append('<option value="">全部</option>');
for (var j = 0; j < hosts.length; j++) {
$('#hosts').append('<option value="' + hosts[j] + '">' + hosts[j] + '</option>');
}
$('#hosts').select2();
}
}
}
}
$('#projects').on('change', function(e) {
pid = e.val;
changeIp(pid, env);
});
$('input[name="env"]').change(function(e) {
env = e.target.value;
changeIp(pid, env);
});
$('#load-btn').click(function() {
ip = $('#hosts').val();
for (var i = 0; i < projects.length; i++) {
var p = projects[i];
if (p._id === pid) {
if (p.monitor && p.monitor.influx && p.monitor.influx.name) {
influxName = p.monitor.influx.name;
} else {
$.gritter.add({
title: '警告',
text: '未配置监控信息!',
time: 2000,
class_name: 'growl-danger'
});
}
}
}
if (influxName) {
$logs.empty();
$.get('/monitor/log/query', {
influxName: influxName,
ip: ip
}, function(data) {
$(data).each(function(i) {
if (i == 0) {
newTime = this.time;
} else if (i == data.length - 1) {
oldTime = this.time;
}
appendLog(this, 0);
});
$logWrap.scrollTop($logWrap[0].scrollHeight);
});
}
});
function layoutResize() {
$('.yoho-log-dark').height($('body').height() - 340);
}
$(window).resize(function() {
layoutResize();
});
layoutResize();
function appendLog(l, i) {
if (l) {
var html = '<p><span class="timer">' + l.time.replace('T', ' ') + '</span>' +
(ip ? '' : '<span class="host">' + l.host + '</span>')
+ '<span class="pid">[' + l.pid + ']</span><span class="level">[' + l.level + ']</span><span class="message">- ' + l.message + '</span></p>';
if (i === 0) {
$logs.prepend(html);
} else {
$logs.append(html);
}
}
}
function loadOld() {
if (loading) {
return;
} else {
loading = true;
var scH = $logWrap[0].scrollHeight;
$.get('/monitor/log/query', {
influxName: influxName,
ip: ip,
oldTime: oldTime
}, function(data) {
loading = false;
if (data && data.length > 0) {
oldTime = data[data.length - 1].time;
}
$(data).each(function() {
appendLog(this, 0);
});
var scH2 = $logWrap[0].scrollHeight;
$logWrap.scrollTop(scH2 - scH);
});
}
}
function loadNew() {
if (loading) {
return;
} else {
loading = true;
$.get('/monitor/log/query', {
influxName: influxName,
ip: ip,
newTime: newTime
}, function(data) {
loading = false;
if (data && data.length > 0) {
newTime = data[0].time;
}
$(data).each(function() {
appendLog(this);
});
});
}
}
var scrollH, scrollTop = 0;
$logWrap.scroll(function(e) {
scrollH = $(this)[0].scrollHeight;
scrollTop = $(this)[0].scrollTop;
if (scrollTop < 10) {
loadOld();
} else if (scrollTop + $logWrap.height() + 50 >= scrollH) {
loadNew();
}
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -19,7 +19,7 @@
<div class="panel-heading">
<p>请配置项目信息.</p>
</div>
<form action="/projects/save" method="POST" data-pjax>
<form action="/projects/save" method="POST" data-pjax>
<input type="hidden" name="_id" value="{{project._id}}">
<div class="panel-body">
<div class="row">
... ... @@ -29,7 +29,8 @@
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Name</label>
<input type="text" name="name" value="{{project.name}}" class="form-control" placeholder="项目名称, 须和gitlab中的名称一致">
<input type="text" name="name" value="{{project.name}}" class="form-control"
placeholder="项目名称, 须和gitlab中的名称一致">
</div>
<!-- form-group -->
</div>
... ... @@ -37,14 +38,16 @@
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Name</label>
<input type="text" name="subname" value="{{project.subname}}" class="form-control" placeholder="项目名称,ex: 前端wap版">
<input type="text" name="subname" value="{{project.subname}}" class="form-control"
placeholder="项目名称,ex: 前端wap版">
</div>
<!-- form-group -->
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Repository Url</label>
<input type="text" name="gitlab" value="{{project.gitlab}}" class="form-control" placeholder="Gitlab 地址">
<input type="text" name="gitlab" value="{{project.gitlab}}" class="form-control"
placeholder="Gitlab 地址">
</div>
<!-- form-group -->
</div>
... ... @@ -61,7 +64,8 @@
<div class="form-group">
<label class="col-lg-2 control-label" style="text-align: right;padding-top: 7px;">构建脚本:</label>
<div class="col-lg-10">
<input type="text" name="scripts[build]" value="{{project.scripts.build}}" class="form-control" placeholder="ex: gulp build && npm install">
<input type="text" name="scripts[build]" value="{{project.scripts.build}}"
class="form-control" placeholder="ex: gulp build && npm install">
</div>
</div>
</div>
... ... @@ -69,7 +73,8 @@
<div class="form-group">
<label class="col-lg-2 control-label" style="text-align: right;padding-top: 7px;">启动脚本:</label>
<div class="col-lg-10">
<input type="text" name="scripts[start]" value="{{project.scripts.start}}" class="form-control" placeholder="ex: pm2 startOrReload process.json">
<input type="text" name="scripts[start]" value="{{project.scripts.start}}"
class="form-control" placeholder="ex: pm2 startOrReload process.json">
</div>
</div>
</div>
... ... @@ -90,7 +95,11 @@
<label class="control-label">目标服务器</label>
<div class="col-sm-12">
{{#each servers.production}}
<div class="checkbox inline-block mr10"><label><input type="checkbox" name="deploy[production][target][{{@index}}]" value="{{host}}" {{#if checked}}checked=""{{/if}}> {{host}}</label></div>
<div class="checkbox inline-block mr10"><label><input type="checkbox"
name="deploy[production][target][{{@index}}]"
value="{{host}}"
{{#if checked}}checked=""{{/if}}> {{host}}
</label></div>
{{/each}}
</div>
</div>
... ... @@ -100,7 +109,9 @@
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">对应Git分支</label>
<input type="text" value="{{project.deploy.production.branchName}}" name="deploy[production][branchName]" placeholder="master" class="form-control">
<input type="text" value="{{project.deploy.production.branchName}}"
name="deploy[production][branchName]" placeholder="master"
class="form-control">
</div>
</div>
</div>
... ... @@ -108,12 +119,14 @@
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">测试URL</label>
<input type="text" value="{{project.deploy.production.testUrl}}" name="deploy[production][testUrl]" placeholder="ex: http://{host}:8080/test" class="form-control">
<input type="text" value="{{project.deploy.production.testUrl}}"
name="deploy[production][testUrl]"
placeholder="ex: http://{host}:8080/test" class="form-control">
</div>
</div>
</div>
</div><!-- tab-pane -->
<div class="tab-pane" id="profile4">
<div class="row">
<div class="col-sm-12">
... ... @@ -121,7 +134,14 @@
<label class="control-label">目标服务器</label>
<div class="col-sm-12">
{{#each servers.test}}
<div class="checkbox inline-block mr10"><label><input type="checkbox" name="deploy[test][target][{{@index}}]" value="{{host}}" {{#if checked}}checked=""{{/if}}> {{host}}</label></div>
<div class="checkbox inline-block mr10">
<label>
<input type="checkbox"
name="deploy[test][target][{{@index}}]"
value="{{host}}"
{{#if checked}}checked=""{{/if}}> {{host}}
</label>
</div>
{{/each}}
</div>
</div>
... ... @@ -131,7 +151,9 @@
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">对应Git分支</label>
<input type="text" value="{{project.deploy.test.branchName}}" name="deploy[test][branchName]" value="" placeholder="master" class="form-control">
<input type="text" value="{{project.deploy.test.branchName}}"
name="deploy[test][branchName]" value="" placeholder="master"
class="form-control">
</div>
</div>
</div>
... ... @@ -139,7 +161,9 @@
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">测试URL</label>
<input type="text" value="{{project.deploy.test.testUrl}}" name="deploy[test][testUrl]" placeholder="ex: http://{host}:8080/test" class="form-control">
<input type="text" value="{{project.deploy.test.testUrl}}"
name="deploy[test][testUrl]" placeholder="ex: http://{host}:8080/test"
class="form-control">
</div>
</div>
</div>
... ... @@ -147,24 +171,50 @@
</div><!-- tab-content -->
</div>
</div>
<div class="row">
<div class="col-sm-12">
<h5 class="lg-title mb10">监控配置</h5>
<ul class="nav nav-tabs nav-primary">
<li class="active"><a href="#influx-info" data-toggle="tab"><strong>InfluxDB</strong></a></li>
</ul>
<div class="tab-content tab-content-primary mb30">
<div class="tab-pane active" id="influx-info">
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">Influxdb Measurement</label>
<input type="text" name="monitor[influx][name]" value="{{project.monitor.influx.name}}"
class="form-control" placeholder="请输入项目日志配置的measurement">
</div>
</div>
</div>
</div><!-- tab-pane -->
</div>
</div>
</div>
<!-- row -->
</div>
<!-- panel-body -->
<div class="panel-footer">
<button type="submit" class="btn btn-primary">保存</button>
<button type="button" class="btn btn-default ml20 go-back">取消</button>
</div>
</form>
<!-- panel-footer -->
</div>
<script>
$(function(){
$(function() {
$(document.body).off().on('submit', 'form[data-pjax]', function(event) {
event.preventDefault(); // stop default submit behavior
$.pjax.submit(event, '#pjax-container', {
type: 'POST'
});
});
$('.go-back').click(function() {
window.history.go(-1);
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -20,7 +20,6 @@
<div class="panel-heading">
<div class="pull-right">
<a class="btn btn-info btn-rounded mr5 log-btn"><i class="fa fa-eye"></i> 查看构建日志</a>
{{!--<a class="btn btn-warning btn-rounded mr5 rollback-btn"><i class="fa fa-reply"></i> 回滚</a>--}}
<a class="btn btn-success btn-rounded mr20 build-btn"><i class="glyphicon glyphicon-plus"></i> 新增构建</a>
<a href="" class="tooltips panel-minimize"><i class="fa fa-minus"></i></a>
</div>
... ...
... ... @@ -14,6 +14,7 @@
<link rel="stylesheet" href="/css/codemirror/theme/ambiance.css">
<link href="/css/morris.css" rel="stylesheet">
<link href="/css/select2.css" rel="stylesheet" />
<link href="/css/jquery.gritter.css" rel="stylesheet" />
<link href="/css/style.datatables.css" rel="stylesheet">
<link href="/css/dataTables.responsive.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet">
... ... @@ -62,6 +63,7 @@
<script src="/js/dataTables.bootstrap.js"></script>
<script src="/js/dataTables.responsive.js"></script>
<script src="/js/select2.min.js"></script>
<script src="/js/jquery.gritter.min.js"></script>
<script src="/assets/layer/layer.js"></script>
<script src="/assets/layer/layer.util.js"></script>
<script src="/js/socket.io-1.4.5.js"></script>
... ...
... ... @@ -14,6 +14,11 @@
<ul class="nav nav-pills nav-stacked nav-menu">
<li class="active"><a href="/index"><i class="fa fa-home"></i> <span>Dashboard</span></a></li>
<li><a href="/projects"><i class="glyphicon glyphicon-th"></i> <span>Projects</span></a></li>
<li class="parent"><a href=""><i class="fa fa-gears"></i> <span>监控中心</span></a>
<ul class="children">
<li><a href="/monitor/log">实时日志</a></li>
</ul>
</li>
<li class="parent"><a href=""><i class="fa fa-gears"></i> <span>Settings</span></a>
<ul class="children">
<li><a href="/servers/setting">Servers</a></li>
... ...
... ... @@ -12,6 +12,9 @@ const helpers = {
},
replace: (str, pattern, replacement) => {
return _.replace(str, new RegExp(pattern), replacement);
},
json: (obj) => {
return JSON.stringify(obj);
}
};
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/3
*/
async function errorHandle(err, ctx) {
let code = err ? 500 : ctx.response.status;
let message = err ? err.toString() : ctx.response.message;
switch (ctx.accepts('json', 'html', 'text')) {
case 'json':
ctx.body = {
code: code,
message: message
};
break;
case 'html':
await ctx.render('action/500', {error: err.stack || err});
break;
default:
break;
}
}
function statusHandle(ctx) {
let status = ctx.response.status;
if (status === 404) {
switch (ctx.accepts('json', 'html', 'text')) {
case 'json':
ctx.body = {
code: status
};
break;
case 'html':
ctx.redirect('/' + status);
break;
default:
break;
}
}
}
export default function(app) {
return async function(ctx, next) {
try {
await next();
statusHandle(ctx);
} catch (err) {
console.error(err.stack || err);
await errorHandle(err, ctx);
}
};
}
\ No newline at end of file
... ...
html, body {
height: 100%;
}
body > section {
height: 100%;
}
body .mainwrapper {
position: absolute;
top: 60px;
bottom: 0;
width: 100%;
}
body .mainwrapper .leftpanel {
bottom: 0;
}
.mainpanel {
height: 100%;
}
.table>thead>tr>th,
.table>tbody>tr>th,
.table>tfoot>tr>th,
... ... @@ -10,4 +33,50 @@
.CodeMirror {
min-height: 100%;
}
.ml20 {
margin-left: 20px;
}
.yoho-log-dark {
background-color: #000;
overflow: auto;
}
.yoho-log-dark p {
font-family: "Courier New";
}
.yoho-log-dark p span {
margin-right: 15px;
}
.yoho-log-dark .timer {
color: #F6F792;
width: 235px;
display: inline-block;
}
.yoho-log-dark .pid {
color: #F5A503;
}
.yoho-log-dark .level {
color: #77C4D3;
}
.yoho-log-dark .level.error {
color: #EA2E49;
}
.yoho-log-dark .message {
color: #DAEDE2;
word-break:break-all;
}
.yoho-log-dark .host {
width: 140px;
display: inline-block;
}
\ No newline at end of file
... ...
#gritter-notice-wrapper {
position:fixed;
top:20px;
right:20px;
top: 45%;
left: 50%;
width:301px;
z-index:9999;
}
... ...