Skip to content
This repository was archived by the owner on Jul 25, 2022. It is now read-only.

Commit 7502aff

Browse files
committed
init
0 parents  commit 7502aff

File tree

8 files changed

+235
-0
lines changed

8 files changed

+235
-0
lines changed

.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*
2+
3+
!/package.json
4+
!/*.js

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
*log*

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM node:8
2+
3+
LABEL maintainer="Jose Quintana <joseluisq.net>"
4+
5+
WORKDIR /
6+
RUN git clone --depth 1 https://github.com/wolfcw/libfaketime.git
7+
WORKDIR /libfaketime/src
8+
RUN make install
9+
10+
ENV WORKDIR=/usr/src/app
11+
ENV LOG_DIR_PATH=/var/log/logger
12+
13+
WORKDIR $WORKDIR
14+
15+
COPY . .
16+
17+
VOLUME [ $WORKDIR ]
18+
19+
ENTRYPOINT npm run dev

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# logger-stream-rotate
2+
3+
> Write log files and rotate them using gzip.
4+
5+
__Status:__ WIP
6+
7+
## Features
8+
9+
- Output your logs to time-rotated log files.
10+
- Optionally gzip the logs once they are no longer current.

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: "3.3"
2+
3+
services:
4+
5+
app:
6+
build:
7+
context: .
8+
dockerfile: Dockerfile
9+
environment:
10+
- "LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1"
11+
- "FAKETIME_NO_CACHE=1"
12+
volumes:
13+
- ./logs:/var/log/logger
14+
networks:
15+
- default

example.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { Logger } = require("./")
2+
3+
const LOG_DIR_PATH = process.env["LOG_DIR_PATH"] || "./logs"
4+
5+
// Usage
6+
const logger = Logger(LOG_DIR_PATH + "/app-%YYYY-%MM-%DD.log", " | ")
7+
8+
function logData(str) {
9+
logger.write(
10+
str, () => console.log("LOG:", str, "completed!")
11+
)
12+
}
13+
14+
setInterval(() => {
15+
logData(["Log entry example", "Timestamp: " + Date.now(), new Date().toISOString()])
16+
}, 1000)

index.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
const fs = require("fs")
2+
const zlib = require("zlib")
3+
4+
const DATE_FORMATS = [
5+
["%YYYY", /\%YYYY/g],
6+
["%MM", /\%MM/g],
7+
["%DD", /\%DD/g],
8+
["%hh", /\%hh/g],
9+
["%mm", /\%mm/g],
10+
["%ss", /\%ss/g],
11+
]
12+
13+
const STREAM_WRITABLE_OPTIONS = { flags: "a", encoding: "utf8" }
14+
15+
function strfdatetime(str, date) {
16+
if (!str) {
17+
return str
18+
}
19+
20+
if (!date) {
21+
date = getNow()
22+
}
23+
24+
const dateFormatValues = [
25+
[DATE_FORMATS[0][0], DATE_FORMATS[0][1], date.getFullYear()],
26+
[DATE_FORMATS[1][0], DATE_FORMATS[1][1], ("0" + date.getMonth()).slice(-2)],
27+
[DATE_FORMATS[2][0], DATE_FORMATS[2][1], ("0" + date.getDate()).slice(-2)],
28+
[DATE_FORMATS[3][0], DATE_FORMATS[3][1], ("0" + date.getHours()).slice(-2)],
29+
[DATE_FORMATS[4][0], DATE_FORMATS[4][1], ("0" + date.getMinutes()).slice(-2)],
30+
[DATE_FORMATS[5][0], DATE_FORMATS[5][1], ("0" + date.getSeconds()).slice(-2)],
31+
]
32+
33+
for (let i = 0; i < dateFormatValues.length; i++) {
34+
const fmt = dateFormatValues[i]
35+
36+
if (str.indexOf(fmt[0]) !== -1) {
37+
str = str.replace(fmt[1], fmt[2])
38+
}
39+
}
40+
41+
return str
42+
}
43+
44+
function createWritableStream(filePath) {
45+
return fs.createWriteStream(filePath, STREAM_WRITABLE_OPTIONS)
46+
}
47+
48+
function getCurrentDate() {
49+
const now = getNow()
50+
51+
return now.getFullYear() + "-" +
52+
("0" + now.getMonth()).slice(-2) + "-" +
53+
("0" + now.getDate()).slice(-2)
54+
}
55+
56+
/**
57+
* Logger stream function
58+
*
59+
* @param {String} logFilePath Log file string path which support MySQL format.
60+
* E.g. my-file-%YYYY-%MM-%DD-%hh:%mm:%ss.log
61+
* @param {*} separator String separator character for parts of one log entry.
62+
*/
63+
function Logger(logFilePath, separator = "|") {
64+
let currentLogFilePath = strfdatetime(logFilePath)
65+
let stream = createWritableStream(currentLogFilePath)
66+
let lastDate = getCurrentDate()
67+
68+
/**
69+
* Writes buffer strings or array of strings into current log stream
70+
*
71+
* @param {String|Array} data Databa to write into current log file
72+
* @param {Function} cb Callback function when current write was finished
73+
*/
74+
function write(data, cb) {
75+
let str = ""
76+
77+
if (typeof data === "string") {
78+
str = data
79+
} else if (Array.isArray(data)) {
80+
str = data.join(separator)
81+
} else {
82+
str = (data || "").toString()
83+
}
84+
85+
const now = getNow()
86+
const timestamp = Date.now()
87+
88+
const currentDate = now.getFullYear() + "-" +
89+
("0" + now.getMonth()).slice(-2) + "-" +
90+
("0" + now.getDate()).slice(-2)
91+
92+
// If dates are different proceed to perform following actions:
93+
// 1. Compress current log file
94+
// 2. Close (end) current writable stream
95+
// 3. Remove current log file
96+
// 4. Close (end) current readable stream
97+
// 5. Create a new log writable stream using the `currentDate` value
98+
if (lastDate !== currentDate) {
99+
const input = fs.createReadStream(currentLogFilePath)
100+
const output = fs.createWriteStream(currentLogFilePath + ".gz")
101+
102+
input.on("end", () => {
103+
stream.end()
104+
105+
fs.unlink(currentLogFilePath, function () { })
106+
input.close()
107+
108+
currentLogFilePath = strfdatetime(logFilePath)
109+
stream = createWritableStream(currentLogFilePath)
110+
111+
const datetime = currentDate + " " +
112+
("0" + now.getHours()).slice(-2) + ":" +
113+
("0" + now.getMinutes()).slice(-2) + ":" +
114+
("0" + now.getSeconds()).slice(-2)
115+
116+
str = datetime + separator + timestamp + separator + str.trim() + "\n"
117+
118+
// Note: respect backpressure and avoid memory issues using the 'drain' event
119+
const isWritted = stream.write(str)
120+
121+
lastDate = currentDate
122+
123+
if (!isWritted) {
124+
stream.once("drain", cb)
125+
} else {
126+
process.nextTick(cb)
127+
}
128+
})
129+
130+
input.pipe(zlib.createGzip()).pipe(output)
131+
} else {
132+
const datetime = currentDate + " " +
133+
("0" + now.getHours()).slice(-2) + ":" +
134+
("0" + now.getMinutes()).slice(-2) + ":" +
135+
("0" + now.getSeconds()).slice(-2)
136+
137+
str = datetime + separator + timestamp + separator + str.trim() + "\n"
138+
139+
// Note: respect backpressure and avoid memory issues using the 'drain' event
140+
const isWritted = stream.write(str)
141+
142+
lastDate = currentDate
143+
144+
if (!isWritted) {
145+
stream.once("drain", cb)
146+
} else {
147+
process.nextTick(cb)
148+
}
149+
}
150+
}
151+
152+
return {
153+
write
154+
}
155+
}
156+
157+
module.exports = {
158+
Logger
159+
}

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "logger-stream-rotate",
3+
"version": "0.0.0",
4+
"main": "index.js",
5+
"private": true,
6+
"license": "MIT",
7+
"scripts": {
8+
"dev": "node example.js"
9+
}
10+
}

0 commit comments

Comments
 (0)