본문 바로가기

Node.js

[Node.js] morgan logger API

최근 개인적으로 Node.js스터디를 진행하고 있습니다. 책을 한권 구해 차근차근 따라가던 중 express의 logger 미들웨어가 동작하지 않더군요. 우선 기본 개념은 인터넷 참조없이 책만으로 해결하려고 마음먹었는데 당황했습니다. 바로 아래와 같은 에러 메세지가 등장합니다.


Error: Most middleware (like logger) is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.


간단히 해석하면 "대부분의 미들웨어(로거와 같은)는 더 이상 익스프레스에 기본으로 제공되지 않으니 반드시 개별로 설치해라. 그리고 url을 봐라"라는군요. (express 4.x버전부터 삭제되었습니다.) logger뿐만 아니라 많은 미들웨어가 제거 되었나봅니다. 앞으로 몇번의 난관이 더 있을거라는 이야기군요.


우선은 express.logger()를 대체할 API를 찾다보니 morgan이라는 모듈이 검색이 됩니다. morgan의 사용법을 알아봅시다.

morgan module을 설치합니다.


npm install morgan

아래 나오는 api는 https://github.com/expressjs/morgan을 참조하였습니다. 거의 없다시피한 영어 실력으로 번역하였으므로, 오역이 상당히 많을것으로 예상됩니다.


API

var morgan = require('morgan');


morgan(format, options)

morgan 로거 미들웨어 함수는 주어진 format, options을 이용하여 생성합니다. format은 미리 정의된 이름의 문자열(아래 참조), 형식 문자열, 또는 로그 항목을 생성하는 함수가 될 수 있습니다.

Options

morgan은 다음과같은 속성을 옵션으로 받아들입니다.

immediate

response대신 request에 따라 로그를 작성합니다. 서버 크래시가 발생하여도 request들은 기록되지만, reponse(response code, 컨텐츠 길이 등등)의 데이터는 기록할 수 없기 때문입니다.

skip

로깅의 스킵여부를 결정하기 위한 함수입니다. 기본값은 false. 이 함수는 "skip(req, res)" 형식으로 호출됩니다.

// 예 : 에러 response만 기록합니다.
morgan('combined', {
     skip : function(req, res) { return res.statusCode < 400 }
});

 실습 해봅시다! 간단히 예제코드 작성해봤습니다.


var http = require('http');
var express = require('express');
var morgan = require('morgan');

var app = express();
var router = express.Router();

// 이곳으로 접근하면 로그를 출력
router.get('/logging', function(req, res) {
	console.log('access logging');
	res.writeHead(404, { 'Content-Type' : 'text/html' });
	res.end('error!!');
});

// 이곳으로 접근하면 로그 출력하지 않음
router.get('/skip', function(req, res) {
	console.log('access skip');
	res.writeHead(200, { 'Content-Type' : 'text/html' });
	res.end('success!!');
});

// Create new Morgan
var myMorgan = morgan('combined', {
	skip : function(req, res) { return res.statusCode < 400 }
});

app.use(myMorgan);
app.use('/', router);

// 서버 생성 및 리슨
http.createServer(app).listen(10000, function() {
	console.log('Server Start!!');
});

위의 코드를 작성하고 서버를 띄운 후 http://localhost:10000/logging 과 http://localhost:10000/skip에 접근하여봅시다.


Server Start!!
access skip
access logging
::1 - - [07/Apr/2015:05:23:26 +0000] "GET /logging HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36"

 서버가 실행되고 먼저 skip으로 접근을 하였습니다. console.log로 출력한 "access skip"이란 메세지 외에는 아무런 로그도 출력되지 않습니다.
 반면 logging으로 접근하면 콘솔로그 외에 별도의 로그가 찍히는것을 확인할 수 있습니다. Morgan의 logging입니다.

stream

로그 작성을 위한 Output stream옵션입니다. 기본값은 process.stdout입니다.

정의된 포맷들

다양한 정의돈 포맷들이 제공됩니다:

combined

표준 Apache combined 로그 출력

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
common

표준 Apache common 로그 출력.

:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]
dev

개발용을 위해 response에 따라 색상이 입혀진 축약된 로그를 출력합니다. :status값이 빨간색이면 서버 에러코드, 노란색이면 클라이언트 에러 코드, 청록색은 리다이렉션 코드, 그외 코드는 컬러가 입없습니다.

:method :url :status :response-time ms - :res[content-length]
short

기본 설정보다 짧은 로그를 출력하고, 응답 시간을 포함합니다.

:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
tiny

최소화된 로그를 출력합니다.

:method :url :status :res[content-length] - :response-time ms

Tokens

새 토큰 생성

토큰을 정의하기 위해서는 단순히 morgan.toke()함수를 이름, 콜백함수와 함께 호출하면 됩니다. 이 콜백함수는 string값을 리턴할것이라고 예상합니다. 아래의 콜백함수에서 리턴하는 값은 ":type"으로 사용할 수 있습니다:

morgan.token('type', function(req, res){ return req.headers['content-type']; })

morgan.toke()함수를 호출할 때 같은 이름을 사용하면 기존 token정의는 덮어쓰여지게 됩니다.

:date[format]

현재의 UTC날짜/시간. 사용 가능한 포맷:

  • clf 일반적인 로그 포맷 ("10/Oct/2000:13:55:36 +0000")
  • iso 공통 ISO 8601 날짜/시간 포맷 (2000-10-10T13:55:36.000Z)
  • web 공통 RFC 1123 날짜/시간 포맷 (Tue, 10 Oct 2000 13:55:36 GMT)

포맷을 설정하지 않을경우 기본값은 web입니다.

:http-version

request의 HTTP버전.

:method

request의 메소드(get,post.....).

:referrer

request의 Referer header.

:remote-addr

request의 remote address. 이 값은 req.ip를 사용한다, 그렇지 않으면 표준req.connection.remoteAddress 값을 사용 (socket address).

:remote-user

인증된 사용자.

:req[header]

The given header 정보.

:res[header]

The given header of the response.

:response-time

request가 morgan으로 들어오고 response header가 쓰여지기 까지의 시간(millis).

:status

reponse status code.

:url

request의 URL. req.originalUrl 이 존재하면 상용하고, 아닐경우 req.url 사용한다.

:user-agent

User-Agent header의 내용.

Examples

express/connect

모든 요청에 대해서 Aapache combinded 포맷의 로그를 출력하는 간단한 앱 예제


var express = require('express')
var morgan = require('morgan')

var app = express()

app.use(morgan('combined'))

app.get('/', function (req, res) {
  res.send('hello, world!')
})

아래는 express모듈을 사용하지 않은 앱의 예제


var finalhandler = require('finalhandler')
var http = require('http')
var morgan = require('morgan')

// create "middleware"
var logger = morgan('combined')

http.createServer(function (req, res) {
  var done = finalhandler(req, res)
  logger(req, res, function (err) {
    if (err) return done(err)

    // respond to request
    res.setHeader('content-type', 'text/plain')
    res.end('hello, world!')
  })
})

사용자 정의 토큰을 사용하기

모든 요청에 ID값을 붙여넣은 후 :id토큰을 이용하여 로그를 출력하는 예제


var express = require('express')
var morgan = require('morgan')
var uuid = require('node-uuid')

morgan.token('id', function getId(req) {
  return req.id
})

var app = express()

app.use(assignId)
app.use(morgan(':id :method :url :response-time'))

app.get('/', function (req, res) {
  res.send('hello, world!')
})

function assignId(req, res, next) {
  req.id = uuid.v4()
  next()
}