Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[KODO-12017] 添加生产模式输出详细日志功能及配置 (#499)
- Loading branch information
Showing
16 changed files
with
458 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
.DS_Store | ||
.vscode | ||
node_modules | ||
bower_components | ||
demo/config.js | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import Logger from './index' | ||
|
||
let isCallReport = false | ||
|
||
jest.mock('./report-v3', () => ({ | ||
reportV3: () => { | ||
isCallReport = true | ||
} | ||
})) | ||
|
||
const originalLog = console.log | ||
const originalWarn = console.warn | ||
const originalError = console.error | ||
|
||
const logMessage: unknown[] = [] | ||
const warnMessage: unknown[] = [] | ||
const errorMessage: unknown[] = [] | ||
|
||
beforeAll(() => { | ||
console.log = jest.fn((...args: unknown[]) => logMessage.push(...args)) | ||
console.warn = jest.fn((...args: unknown[]) => warnMessage.push(...args)) | ||
console.error = jest.fn((...args: unknown[]) => errorMessage.push(...args)) | ||
}) | ||
|
||
afterAll(() => { | ||
console.log = originalLog | ||
console.warn = originalWarn | ||
console.error = originalError | ||
}) | ||
|
||
describe('test logger', () => { | ||
test('test level', () => { | ||
const infoLogger = new Logger('', true, 'INFO') | ||
infoLogger.info('test1') | ||
expect(logMessage).toStrictEqual([`Qiniu-JS-SDK [INFO][1]: `, 'test1']) | ||
infoLogger.warn('test2') | ||
expect(warnMessage).toStrictEqual(['Qiniu-JS-SDK [WARN][1]: ', 'test2']) | ||
infoLogger.error('test3') | ||
expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][1]: ', 'test3']) | ||
|
||
// 清空消息 | ||
logMessage.splice(0, logMessage.length) | ||
warnMessage.splice(0, warnMessage.length) | ||
errorMessage.splice(0, errorMessage.length) | ||
|
||
const warnLogger = new Logger('', true, 'WARN') | ||
warnLogger.info('test1') | ||
expect(logMessage).toStrictEqual([]) | ||
warnLogger.warn('test2') | ||
expect(warnMessage).toStrictEqual(['Qiniu-JS-SDK [WARN][2]: ', 'test2']) | ||
warnLogger.error('test3') | ||
expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][2]: ', 'test3']) | ||
|
||
// 清空消息 | ||
logMessage.splice(0, logMessage.length) | ||
warnMessage.splice(0, warnMessage.length) | ||
errorMessage.splice(0, errorMessage.length) | ||
|
||
const errorLogger = new Logger('', true, 'ERROR') | ||
errorLogger.info('test1') | ||
expect(logMessage).toStrictEqual([]) | ||
errorLogger.warn('test2') | ||
expect(warnMessage).toStrictEqual([]) | ||
errorLogger.error('test3') | ||
expect(errorMessage).toStrictEqual(['Qiniu-JS-SDK [ERROR][3]: ', 'test3']) | ||
|
||
// 清空消息 | ||
logMessage.splice(0, logMessage.length) | ||
warnMessage.splice(0, warnMessage.length) | ||
errorMessage.splice(0, errorMessage.length) | ||
|
||
const offLogger = new Logger('', true, 'OFF') | ||
offLogger.info('test1') | ||
expect(logMessage).toStrictEqual([]) | ||
offLogger.warn('test2') | ||
expect(warnMessage).toStrictEqual([]) | ||
offLogger.error('test3') | ||
expect(errorMessage).toStrictEqual([]) | ||
}) | ||
|
||
test('test unique id', () => { | ||
// @ts-ignore | ||
const startId = Logger.id | ||
new Logger('', true, 'OFF') | ||
new Logger('', true, 'OFF') | ||
const last = new Logger('', true, 'OFF') | ||
// @ts-ignore | ||
expect(last.id).toStrictEqual(startId + 3) | ||
}) | ||
|
||
test('test report', () => { | ||
const logger1 = new Logger('', false, 'OFF') | ||
logger1.report(null as any) | ||
expect(isCallReport).toBeTruthy() | ||
isCallReport = false | ||
const logger2 = new Logger('', true, 'OFF') | ||
logger2.report(null as any) | ||
expect(isCallReport).toBeFalsy() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { reportV3, V3LogInfo } from './report-v3' | ||
|
||
export type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'OFF' | ||
|
||
export default class Logger { | ||
private static id: number = 0 | ||
|
||
// 为每个类分配一个 id | ||
// 用以区分不同的上传任务 | ||
private id = ++Logger.id | ||
|
||
constructor( | ||
private token: string, | ||
private disableReport = true, | ||
private level: LogLevel = 'OFF' | ||
) { } | ||
|
||
/** | ||
* @param {V3LogInfo} data 上报的数据。 | ||
* @param {boolean} retry 重试次数,可选,默认为 3。 | ||
* @description 向服务端上报统计信息。 | ||
*/ | ||
report(data: V3LogInfo, retry?: number) { | ||
if (this.disableReport) return | ||
try { reportV3(this.token, data, retry) } | ||
catch (error) { console.warn(error) } | ||
} | ||
|
||
/** | ||
* @param {unknown[]} ...args | ||
* @description 输出 info 级别的调试信息。 | ||
*/ | ||
info(...args: unknown[]) { | ||
const allowLevel: LogLevel[] = ['INFO'] | ||
if (allowLevel.includes(this.level)) { | ||
console.log(`Qiniu-JS-SDK [INFO][${this.id}]: `, ...args) | ||
} | ||
} | ||
|
||
/** | ||
* @param {unknown[]} ...args | ||
* @description 输出 warn 级别的调试信息。 | ||
*/ | ||
warn(...args: unknown[]) { | ||
const allowLevel: LogLevel[] = ['INFO', 'WARN'] | ||
if (allowLevel.includes(this.level)) { | ||
console.warn(`Qiniu-JS-SDK [WARN][${this.id}]: `, ...args) | ||
} | ||
} | ||
|
||
/** | ||
* @param {unknown[]} ...args | ||
* @description 输出 error 级别的调试信息。 | ||
*/ | ||
error(...args: unknown[]) { | ||
const allowLevel: LogLevel[] = ['INFO', 'WARN', 'ERROR'] | ||
if (allowLevel.includes(this.level)) { | ||
console.error(`Qiniu-JS-SDK [ERROR][${this.id}]: `, ...args) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { reportV3, V3LogInfo } from './report-v3' | ||
|
||
class MockXHR { | ||
sendData: string | ||
openData: string[] | ||
openCount: number | ||
headerData: string[] | ||
|
||
status: number | ||
readyState: number | ||
onreadystatechange() { } | ||
|
||
clear() { | ||
this.sendData = '' | ||
this.openData = [] | ||
this.headerData = [] | ||
|
||
this.status = 0 | ||
this.readyState = 0 | ||
} | ||
|
||
open(...args: string[]) { | ||
this.clear() | ||
this.openCount += 1 | ||
this.openData = args | ||
} | ||
|
||
send(args: string) { | ||
this.sendData = args | ||
} | ||
|
||
setRequestHeader(...args: string[]) { | ||
this.headerData.push(...args) | ||
} | ||
|
||
changeStatusAndState(readyState: number, status: number) { | ||
this.status = status | ||
this.readyState = readyState | ||
this.onreadystatechange() | ||
} | ||
} | ||
|
||
const mockXHR = new MockXHR() | ||
|
||
jest.mock('../utils', () => ({ | ||
createXHR: () => mockXHR, | ||
getAuthHeaders: (t: string) => t | ||
})) | ||
|
||
describe('test report-v3', () => { | ||
const testData: V3LogInfo = { | ||
code: 200, | ||
reqId: 'reqId', | ||
host: 'host', | ||
remoteIp: 'remoteIp', | ||
port: 'port', | ||
duration: 1, | ||
time: 1, | ||
bytesSent: 1, | ||
upType: 'jssdk-h5', | ||
size: 1 | ||
} | ||
|
||
test('test stringify send Data', () => { | ||
reportV3('token', testData, 3) | ||
mockXHR.changeStatusAndState(0, 0) | ||
expect(mockXHR.sendData).toBe([ | ||
testData.code || '', | ||
testData.reqId || '', | ||
testData.host || '', | ||
testData.remoteIp || '', | ||
testData.port || '', | ||
testData.duration || '', | ||
testData.time || '', | ||
testData.bytesSent || '', | ||
testData.upType || '', | ||
testData.size || '' | ||
].join(',')) | ||
}) | ||
|
||
test('test retry', () => { | ||
mockXHR.openCount = 0 | ||
reportV3('token', testData) | ||
for (let index = 1; index <= 10; index++) { | ||
mockXHR.changeStatusAndState(4, 0) | ||
} | ||
expect(mockXHR.openCount).toBe(4) | ||
|
||
mockXHR.openCount = 0 | ||
reportV3('token', testData, 4) | ||
for (let index = 1; index < 10; index++) { | ||
mockXHR.changeStatusAndState(4, 0) | ||
} | ||
expect(mockXHR.openCount).toBe(5) | ||
|
||
mockXHR.openCount = 0 | ||
reportV3('token', testData, 0) | ||
for (let index = 1; index < 10; index++) { | ||
mockXHR.changeStatusAndState(4, 0) | ||
} | ||
expect(mockXHR.openCount).toBe(1) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { createXHR, getAuthHeaders } from '../utils' | ||
|
||
export interface V3LogInfo { | ||
code: number | ||
reqId: string | ||
host: string | ||
remoteIp: string | ||
port: string | ||
duration: number | ||
time: number | ||
bytesSent: number | ||
upType: 'jssdk-h5' | ||
size: number | ||
} | ||
|
||
/** | ||
* @param {string} token 上传使用的 token | ||
* @param {V3LogInfo} data 上报的统计数据 | ||
* @param {number} retry 重试的次数,默认值 3 | ||
* @description v3 版本的日志上传接口,参考文档 https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3。 | ||
*/ | ||
export function reportV3(token: string, data: V3LogInfo, retry = 3) { | ||
const xhr = createXHR() | ||
xhr.open('POST', 'https://uplog.qbox.me/log/3') | ||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') | ||
xhr.setRequestHeader('Authorization', getAuthHeaders(token).Authorization) | ||
xhr.onreadystatechange = () => { | ||
if (xhr.readyState === 4 && xhr.status !== 200 && retry > 0) { | ||
reportV3(token, data, retry - 1) | ||
} | ||
} | ||
|
||
// 顺序参考:https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3 | ||
const stringifyData = [ | ||
data.code || '', | ||
data.reqId || '', | ||
data.host || '', | ||
data.remoteIp || '', | ||
data.port || '', | ||
data.duration || '', | ||
data.time || '', | ||
data.bytesSent || '', | ||
data.upType || '', | ||
data.size || '' | ||
].join(',') | ||
|
||
xhr.send(stringifyData) | ||
} |
Oops, something went wrong.