-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add stack trace to log #5
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
import {ILayout} from "./ILayout"; | ||
import {ILayout, IDataFormatFunction} from "./ILayout"; | ||
import {LogEntry} from "./LogEntry"; | ||
import {ILayoutFunction} from "./ILayout"; | ||
|
||
export interface IAppender { | ||
setLayout(layout: ILayout); | ||
setLayoutFunction(layout: ILayoutFunction); | ||
setLayoutFunction(layout: ILayoutFunction, format_data: IDataFormatFunction); | ||
append(entry: LogEntry); | ||
clear(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,15 @@ | ||
import {LogEntry} from "./LogEntry"; | ||
|
||
export interface ILayout { | ||
format(entry: LogEntry): string; | ||
format(entry: LogEntry, include_data: boolean): string; | ||
|
||
formatData(entry: LogEntry): string; | ||
} | ||
|
||
export interface ILayoutFunction { | ||
(entry: LogEntry, include_data: boolean): string; | ||
} | ||
|
||
export interface IDataFormatFunction { | ||
(entry: LogEntry): string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,10 @@ import {LogLevel} from "./LogLevel"; | |
|
||
export interface LogEntry { | ||
level: LogLevel; | ||
object: any; | ||
deep: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant field in context of other comments |
||
time: Date; | ||
message: string; | ||
tag: string; | ||
stack: any; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ import {HTMLLayoutColorTheme} from "./layouts/HTMLLayout"; | |
import {HTMLLayoutColors} from "./layouts/HTMLLayout"; | ||
|
||
export default class LoggerConfig { | ||
constructor(appender?: IAppender, private level: LogLevel = LogLevel.INFO, private tags?: string[]) { | ||
constructor(appender?: IAppender, private level: LogLevel = LogLevel.INFO, private capture_stack: boolean = true, private tags?: string[]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should add new optional parameters to the end of constructor to prevent backward compatibility problems |
||
if (appender) { | ||
this.addAppender(appender); | ||
} | ||
|
@@ -30,6 +30,10 @@ export default class LoggerConfig { | |
return this.level; | ||
} | ||
|
||
public captureStack():boolean { | ||
return this.capture_stack; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The good idea to pass not only flag, but minimal level for displaying stacktraces. For example, you could print stacktraces for WARNING level and higher. |
||
} | ||
|
||
public hasTag(tag: string) { | ||
if (!this.tags || this.tags.length === 0) return true; | ||
|
||
|
@@ -46,7 +50,7 @@ export default class LoggerConfig { | |
private appenders: IAppender[] = []; | ||
|
||
public static createFromJson(json: ConfigJson): LoggerConfig { | ||
let config = new LoggerConfig(null, LogLevel[json.level], json.tags); | ||
let config = new LoggerConfig(null, LogLevel[json.level], json.capture_stack, json.tags); | ||
for (let layout_json of json.layouts) { | ||
let layout: ILayout; | ||
|
||
|
@@ -92,6 +96,7 @@ export default class LoggerConfig { | |
|
||
export interface ConfigJson { | ||
layouts: ConfigJsonLayout[]; | ||
capture_stack:boolean; | ||
level: "ALL" | "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL" | "OFF"; | ||
tags: string[]; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,16 @@ | ||
import {ILayout, ILayoutFunction, IDataFormatFunction} from "../ILayout"; | ||
|
||
import {ILayout} from "../ILayout"; | ||
import {ILayoutFunction} from "../ILayout"; | ||
export default class BaseAppender { | ||
setLayout(layout: ILayout) { | ||
setLayout(layout:ILayout) { | ||
this.layout = layout; | ||
} | ||
setLayoutFunction(layout: ILayoutFunction) { | ||
|
||
setLayoutFunction(layout:ILayoutFunction, format_data: IDataFormatFunction) { | ||
this.layout = { | ||
format: layout | ||
format: layout, | ||
formatData: format_data | ||
} | ||
} | ||
|
||
protected layout: ILayout; | ||
protected layout:ILayout; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,53 @@ | ||
import {IAppender} from "../IAppender"; | ||
import {ILayout} from "../ILayout"; | ||
import {LogEntry} from "../LogEntry"; | ||
import BaseAppender from "./BaseAppender"; | ||
|
||
export default class ConsoleAppender extends BaseAppender implements IAppender { | ||
append(entry:LogEntry) { | ||
console.log(this.layout.format(entry)); | ||
if (!console || !console.log) { | ||
return; | ||
} | ||
|
||
if (!this.isGroupingAvailable()) { | ||
this.simpleAppend(entry); | ||
} else { | ||
this.complexAppend(entry); | ||
} | ||
} | ||
|
||
clear() { | ||
console.clear(); | ||
} | ||
|
||
protected simpleAppend(entry:LogEntry) { | ||
console.log(this.layout.format(entry, true)); | ||
} | ||
|
||
protected complexAppend(entry:LogEntry) { | ||
|
||
this.startGroup(this.layout.format(entry, false)); | ||
|
||
if (entry.object) { | ||
this.startGroup("Additional Data"); | ||
|
||
console.log(this.layout.formatData(entry)); | ||
|
||
console.groupEnd(); | ||
} | ||
|
||
console.log(entry.stack); | ||
console.groupEnd(); | ||
} | ||
|
||
protected startGroup(msg: any) { | ||
if(console.groupCollapsed) { | ||
console.groupCollapsed(msg); | ||
} else { | ||
console.group(msg); | ||
} | ||
} | ||
|
||
protected isGroupingAvailable(): boolean { | ||
return !!console.groupCollapsed || !!console.group | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,26 @@ | ||
import {ILayout} from "../ILayout"; | ||
import {LogEntry} from "../LogEntry"; | ||
import {logLevelToString, LogLevel} from "../LogLevel"; | ||
import {logLevelToString} from "../LogLevel"; | ||
import {stringify} from "../Utils"; | ||
|
||
/** | ||
* Simple layout, that formats logs as | ||
* "{time} {level} [{tag}] - {message}" | ||
*/ | ||
export default class BasicLayout implements ILayout { | ||
format(entry:LogEntry):string { | ||
return this.formatDate(entry.time) + ' ' + logLevelToString(entry.level) + ' [' + entry.tag + '] - ' + entry.message; | ||
format(entry:LogEntry, include_data: boolean):string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea was to keep API simple and mimic the log4j and others. I think, passing some flags (especially when they lead us to repeating code in |
||
return `${this.formatDate(entry.time)} ${logLevelToString(entry.level)} [${entry.tag}] - ${entry.message}${include_data? " " + this.formatData(entry): ''}`; | ||
} | ||
|
||
private formatDate(date: Date): string { | ||
formatData(entry:LogEntry):string { | ||
if (typeof entry.object !== "undefined") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code would be copy&pasted in every appender created |
||
return stringify(entry.object, entry.deep || 1); | ||
} | ||
|
||
return ''; | ||
} | ||
|
||
private formatDate(date:Date):string { | ||
function pad(number) { | ||
if (number < 10) { | ||
return '0' + number; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import {ILayout} from "../ILayout"; | ||
import {LogEntry} from "../LogEntry"; | ||
import {LogLevel} from "../LogLevel"; | ||
import {stringify} from "../Utils"; | ||
|
||
export interface HTMLLayoutColors { | ||
tag: string; | ||
|
@@ -16,6 +17,7 @@ export enum HTMLLayoutColorTheme { | |
} | ||
|
||
export default class HTMLLayout implements ILayout { | ||
|
||
constructor(colors_theme?: HTMLLayoutColorTheme | HTMLLayoutColors) { | ||
if (colors_theme === HTMLLayoutColorTheme.LIGHT) { | ||
this.colors = { | ||
|
@@ -42,11 +44,40 @@ export default class HTMLLayout implements ILayout { | |
this.colors = <HTMLLayoutColors>colors_theme; | ||
} | ||
} | ||
format(entry:LogEntry):string { | ||
return '<span' + this.getTimeStyle() + '>' + this.formatDate(entry.time) + '</span> ' + | ||
format(entry:LogEntry, include_data: boolean):string { | ||
let res = '<span' + this.getTimeStyle() + '>' + this.formatDate(entry.time) + '</span> ' + | ||
'<span' + this.getLevelStyle() + '>' + LogLevel[entry.level] + '</span> ' + | ||
'<span' + this.getTagStyle() + '>[' + entry.tag + ']</span> ' + | ||
'<span' + this.getMessageStyle() + '>' + entry.message + '</span>'; | ||
'<span' + this.getMessageStyle() + '>' + entry.message + '</span>'; | ||
|
||
if(include_data) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of passing the flag, why not just check for entry.object? |
||
res += '<pre>' + this.formatData(entry) + '</pre>'; | ||
} | ||
|
||
if(entry.stack) { | ||
let formatted = this.formatStack(entry.stack); | ||
|
||
if(formatted) { | ||
res += '<pre>' + formatted + '</pre>'; | ||
} | ||
} | ||
|
||
return res; | ||
} | ||
|
||
formatData(entry:LogEntry):string { | ||
if (typeof entry.object !== "undefined") { | ||
return stringify(entry.object, entry.deep || 1); | ||
} | ||
} | ||
|
||
private formatStack(stack: any):string { | ||
|
||
if(stack && stack.split) { | ||
return stack.split('\n').join('<br />'); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private getTimeStyle() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separating text and object is a good idea, but I insist on passing object data as string, not as object itself. It would give some overhead in JSON serializing/unserializing, but only in console appender