基于Node.js和express的日志服务器

        首先,这篇文章学习的意义大于实际价值。如果按我的本意,直接在游戏中加入友盟,信息更全,而且非常简单。不过总是有很多人会凭着自己过时或者错误的经验去说别的东西多么不好,自己的东西多么好。好在,我自认为学习能力非常强,解决问题的能力也非常强。真让我做一个服务器+前端,也是在兴趣之中和能力之内。

     

一、Node.js简介 

      原本javascript纯粹是一个前端语言,干的基本上是让网页更丰富更炫的事情。不过Node.js出现后,javacript成为了前后端通吃的语言。比如网易的pomelo就是基于Node.js的游戏服务器引擎,全世界很多大公司也都开始使用Node.js构建他们的服务器后端。

      Node.js有这么几个特点:

      1、基于javacript语言,群众基础非常扎实,github上面70%的库都是javacript写的,那些star过万的库,也几乎清一色的javascript

      2、基于google的V8 javascript解释引擎,速度非常快,作为服务器语言,可以不是最快的,但是一定不能是慢的

      3、异步+回调的操作,使得Node.js的服务器的并发数目非常高

      4、npm和社区庞大,Node.js的第三方库多到无法想象


二、express简介

       express是Node.js中非常流行的Http服务器框架。通过express,你可以快速的搭建好一个http服务器,从而接收和处理‘GET‘ ‘POST‘等请求。

       通过express-generator可以生成一个基础的express服务器工程。


三、log服务器代码

app.js代码:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var morgan = require('morgan');
var routes = require('./routes/index');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(morgan('dev'));

// uncomment after placing your favicon in /public
app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'), { noCache:1, maxAge: 0 }));

app.use('/', routes);

//app.disable('etag');

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.listen(8080)

module.exports = app;

index.js代码:

var express = require('express');
var router = express.Router();
var fs = require('fs')

/* GET home page. */
router.get('/', function(req, res) {
  fs.readFile('logs/all-logs.log', function(err, data) {
    if (err) {
      var list = ['file not find']
      res.render('index', { title: 'Log Server', logs: list});
    } else {
      var list = data.toString().split('\n');
      res.render('index', { title: 'Log Server', logs: list});
    }
  });
});

var winston = require('winston');
var logger = new winston.Logger({
  transports: [
    new winston.transports.File({
      level: 'info',
      filename: './logs/all-logs.log',
      handleExceptions: true,
      json: true,
      maxsize: 5242880, //5MB
      maxFiles: 5,
      colorize: false
    }),
    new winston.transports.Console({
      level: 'debug',
      handleExceptions: true,
      json: false,
      colorize: true
    })
  ],
  exitOnError: false
});

router.post('/', function(req, res) {
  logger.log('info', req.body)
  // req.body.log   req.body.platform  req.body.stack
  res.send('1');
});

module.exports = router;

客户端中发送错误日志:

local csdebug = {}

function csdebug.send(fmt, ...)
    local log = string.format(tostring(fmt), ...);
    local platformCode = cc.Application:getInstance():getTargetPlatform();
    local platform = 'unknown';
    if platformCode == 0 then
    	platform = 'windows'
    elseif platformCode == 3 then
    	platform = 'android'
    elseif platformCode == 4 then
    	platform = 'ios';
    elseif platformCode == 5 then
    	platform = 'ipad'
    end

    local xhr = cc.XMLHttpRequest:new()
    xhr.responseType = cc.XMLHTTPREQUEST_RESPONSE_STRING
    xhr:open("POST", "http://"..GameConfig.LogServerIp)
    xhr:setRequestHeader('Content-Type', 'application/json')
    local function onReadyStateChange()
        if xhr.readyState == 4 and (xhr.status >= 200 and xhr.status < 207) then
            print(xhr.response)
        else
            print("xhr.readyState is:", xhr.readyState, "xhr.status is: ",xhr.status)
        end
    end

    xhr:registerScriptHandler(onReadyStateChange)
    local cjson = require('cjson');

	local json = cjson.encode({log=log, platform=platform, stack=debug.traceback("", 3)});
	print(json)
	--local json = string.format('{"log":"%s","platform":"%s"}', log, platform, debug.traceback("", 3));
    xhr:send(json)
end

return csdebug
最关键的是cocos2d-x中HttpRequest的使用。我们直接通过send中发送body内容,则字符串不需要进行url-encoding。而且node.js的服务器接收到的就是json数据,可以直接使用其内容。

在打印错误日志的函数中以及main.lua的__G__TRACKBACK__函数中调用上面的函数,则可以把错误日志发送到服务器。

四、一些解释说明:

1、express最基本的功能是网页路由(route),说白了就是向服务器请求对应地址时,服务器应该做什么。比如index.js就是我们log的核心功能。index.js在routes文件夹下。我们将index.js挂接在‘/‘根目录,也就是说,当我们直接访问"http://localhost:8888"的时候会调用index.js相关的函数。我们在index.js中分别通过router.get和router.post挂接了两个处理函数,用来处理:a、当浏览器访问网页时,读取log文件并输出,这个是用来查看日志内容的。   b、当我们在客户端通过post发送日志内容到服务器的时候,通过log模块记录其到文本中。

2、express中通过模板引擎把后端数据和前端网页渲染结合在一起。模板放在views里面。比较出名的模板引擎有jade(express自带)、ejs、hbs等等。所谓模板引擎就是一个解释器,它定义了一种格式,我们通过对应的规则书写网页模板,而模板引擎会自动把它们编译成html最终交给客户端(浏览器)来显示。这里我使用的是ejs,它的语法跟html比较像。

3、运行服务器就是直接在命令行中输入:node app。如果使用webstorm则直接调试app.js就可以了,webstorm对node.js的支持非常好。调试什么的都很完美。

4、平常开发时可以使用 nodemon app来运行服务器。nodemon是一个第三方库,需要安装。它的功能是检测js文件改变,如果发生改变则自动重启服务器。通过它,你可以所写即所得的开发服务器,而不需要不停的重启服务器。

5、关于浏览器缓存。这个可以参考这篇文章,说的非常清晰。最傻瓜的方式是使用F5来刷新网页,如果像我一开始一样习惯在地址栏按回车打开网页,则很有可能受浏览器缓存的影响,看到的都是旧的内容(无论这个时候服务器有没有更新)。使用F5来刷新网页则会忽略浏览器缓存强制刷新。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。