| @@ -0,0 +1,21 | |||||
|
|
1 | import * as TraceSource from './TraceSource' | |||
|
|
2 | ||||
|
|
3 | class TraceEvent { | |||
|
|
4 | readonly source: TraceSource; | |||
|
|
5 | ||||
|
|
6 | readonly level: Number; | |||
|
|
7 | ||||
|
|
8 | readonly arg: any; | |||
|
|
9 | ||||
|
|
10 | constructor(source: TraceSource, level: Number, arg: any) { | |||
|
|
11 | this.source = source; | |||
|
|
12 | this.level = level; | |||
|
|
13 | this.arg = arg; | |||
|
|
14 | } | |||
|
|
15 | } | |||
|
|
16 | ||||
|
|
17 | namespace TraceEvent { | |||
|
|
18 | ||||
|
|
19 | } | |||
|
|
20 | ||||
|
|
21 | export = TraceEvent No newline at end of file | |||
| @@ -0,0 +1,29 | |||||
|
|
1 | import { IObservable } from "../../interfaces"; | |||
|
|
2 | import * as TraceEvent from '../TraceEvent'; | |||
|
|
3 | import { ICancellation } from "../../interfaces"; | |||
|
|
4 | import { Cancellation } from "../../Cancellation"; | |||
|
|
5 | import * as TraceSource from "../TraceSource"; | |||
|
|
6 | ||||
|
|
7 | class ConsoleWriter { | |||
|
|
8 | async write(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) { | |||
|
|
9 | let next; | |||
|
|
10 | while(next = await source.next(ct)) { | |||
|
|
11 | this._writeEvent(next); | |||
|
|
12 | } | |||
|
|
13 | } | |||
|
|
14 | ||||
|
|
15 | private _writeEvent(next: TraceEvent) { | |||
|
|
16 | if (next.level >= TraceSource.LogLevel) { | |||
|
|
17 | console.log(next.source.toString(), next.arg); | |||
|
|
18 | } else if(next.level >= TraceSource.WarnLevel) { | |||
|
|
19 | console.warn(next.source.toString(), next.arg); | |||
|
|
20 | } else { | |||
|
|
21 | console.error(next.source.toString(), next.arg); | |||
|
|
22 | } | |||
|
|
23 | } | |||
|
|
24 | } | |||
|
|
25 | ||||
|
|
26 | namespace ConsoleWriter { | |||
|
|
27 | } | |||
|
|
28 | ||||
|
|
29 | export = ConsoleWriter; No newline at end of file | |||
| @@ -1,23 +1,29 | |||||
| 1 |
import { IObservable |
|
1 | import { IObservable, IDestroyable, ICancellation } from '../interfaces'; | |
| 2 | import { Cancellation } from '../Cancellation' |
|
2 | import { Cancellation } from '../Cancellation' | |
| 3 | import * as TraceSource from '../log/TraceSource' |
|
|||
| 4 | import { argumentNotNull } from '../safe'; |
|
3 | import { argumentNotNull } from '../safe'; | |
| 5 |
|
4 | |||
| 6 | const trace = TraceSource.get('@implab/core/components/Observable'); |
|
|||
| 7 |
|
||||
| 8 |
|
5 | |||
| 9 | class Observable<T> implements IObservable<T> { |
|
6 | interface Handler<T> { | |
| 10 | private _once = new Array<IObserver<T>>(); |
|
7 | (x:T) : void | |
|
|
8 | } | |||
| 11 |
|
9 | |||
| 12 | private readonly _observers = new Array<IObserver<T>>(); |
|
10 | interface Initializer<T> { | |
|
|
11 | (notify: Handler<T>) : (() => void) | void; | |||
|
|
12 | } | |||
|
|
13 | ||||
| 13 |
|
14 | |||
| 14 | constructor(func: (notify: IObserver<T>) => void) { |
|
15 | class Observable<T> implements IObservable<T>, IDestroyable { | |
| 15 | argumentNotNull(func, "func"); |
|
16 | private _once = new Array<Handler<T>>(); | |
|
|
17 | ||||
|
|
18 | private readonly _observers = new Array<Handler<T>>(); | |||
| 16 |
|
19 | |||
| 17 | func(this._notify.bind(this)); |
|
20 | private readonly _cleanup : (() => void) | void; | |
|
|
21 | ||||
|
|
22 | constructor(func?: Initializer<T>) { | |||
|
|
23 | this._cleanup = func && func(this._notify.bind(this)); | |||
| 18 | } |
|
24 | } | |
| 19 |
|
25 | |||
| 20 | on(observer: IObserver<T>): IDestroyable { |
|
26 | on(observer: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable { | |
| 21 | argumentNotNull(observer, "observer"); |
|
27 | argumentNotNull(observer, "observer"); | |
| 22 |
|
28 | |||
| 23 | this._observers.push(observer); |
|
29 | this._observers.push(observer); | |
| @@ -30,7 +36,7 class Observable<T> implements IObservab | |||||
| 30 | } |
|
36 | } | |
| 31 | } |
|
37 | } | |
| 32 |
|
38 | |||
| 33 |
|
|
39 | next(ct: ICancellation = Cancellation.none): Promise<T> { | |
| 34 | return new Promise<T>((resolve, reject) => { |
|
40 | return new Promise<T>((resolve, reject) => { | |
| 35 | this._once.push(resolve); |
|
41 | this._once.push(resolve); | |
| 36 | if (ct.isSupported()) { |
|
42 | if (ct.isSupported()) { | |
| @@ -42,24 +48,28 class Observable<T> implements IObservab | |||||
| 42 | }); |
|
48 | }); | |
| 43 | } |
|
49 | } | |
| 44 |
|
50 | |||
| 45 | onObserverException(e: any) { |
|
51 | destroy() { | |
| 46 | trace.error("Unhandled exception in the observer: {0}", e); |
|
52 | if(this._cleanup) | |
|
|
53 | this._cleanup.call(null); | |||
| 47 | } |
|
54 | } | |
| 48 |
|
55 | |||
| 49 | private _removeOnce(d: IObserver<T>) { |
|
56 | protected onObserverException(e: any) { | |
|
|
57 | } | |||
|
|
58 | ||||
|
|
59 | private _removeOnce(d: Handler<T>) { | |||
| 50 | let i = this._once.indexOf(d); |
|
60 | let i = this._once.indexOf(d); | |
| 51 | if (i >= 0) |
|
61 | if (i >= 0) | |
| 52 | this._once.splice(i); |
|
62 | this._once.splice(i); | |
| 53 | } |
|
63 | } | |
| 54 |
|
64 | |||
| 55 |
private _removeObserver(d: |
|
65 | private _removeObserver(d: Handler<T>) { | |
| 56 | let i = this._observers.indexOf(d); |
|
66 | let i = this._observers.indexOf(d); | |
| 57 | if (i >= 0) |
|
67 | if (i >= 0) | |
| 58 | this._observers.splice(i); |
|
68 | this._observers.splice(i); | |
| 59 | } |
|
69 | } | |
| 60 |
|
70 | |||
| 61 |
pr |
|
71 | protected _notify(evt: T) { | |
| 62 |
let guard = (observer: |
|
72 | let guard = (observer: Handler<T>) => { | |
| 63 | try { |
|
73 | try { | |
| 64 | observer(evt); |
|
74 | observer(evt); | |
| 65 | } catch (e) { |
|
75 | } catch (e) { | |
| @@ -79,7 +89,6 class Observable<T> implements IObservab | |||||
| 79 | } |
|
89 | } | |
| 80 |
|
90 | |||
| 81 | namespace Observable { |
|
91 | namespace Observable { | |
| 82 | export const traceSource = trace; |
|
|||
| 83 | } |
|
92 | } | |
| 84 |
|
93 | |||
| 85 | export = Observable; No newline at end of file |
|
94 | export = Observable; | |
| @@ -1,5 +1,3 | |||||
| 1 | import { watchFile } from "fs"; |
|
|||
| 2 |
|
||||
| 3 |
|
|
1 | export interface IDestroyable { | |
| 4 | destroy(); |
|
2 | destroy(); | |
| 5 | } |
|
3 | } | |
| @@ -72,12 +70,7 export interface ICancellable { | |||||
| 72 | cancel(reason?: any): void; |
|
70 | cancel(reason?: any): void; | |
| 73 | } |
|
71 | } | |
| 74 |
|
72 | |||
| 75 | export interface IObserver<T> { |
|
|||
| 76 | (x:T): void; |
|
|||
| 77 | } |
|
|||
| 78 |
|
||||
| 79 | export interface IObservable<T> { |
|
73 | export interface IObservable<T> { | |
| 80 | on(observer: IObserver<T>): IDestroyable; |
|
74 | on(next: (x:T) => void, error?: (e:any) => void, complete?:() => void): IDestroyable; | |
| 81 |
|
75 | next(ct?: ICancellation) : Promise<T>; | ||
| 82 | wait(ct?: ICancellation) : Promise<T>; |
|
|||
| 83 | } No newline at end of file |
|
76 | } | |
| @@ -1,17 +1,8 | |||||
| 1 | import * as format from '../text/format' |
|
1 | import * as format from '../text/format' | |
| 2 | import { argumentNotNull } from '../safe'; |
|
2 | import { argumentNotNull } from '../safe'; | |
| 3 |
|
3 | import * as Observable from '../components/Observable' | ||
| 4 | interface TraceEventHandler { |
|
4 | import { IDestroyable } from '../interfaces'; | |
| 5 | (sender: TraceSource, level: number, arg: any): void; |
|
5 | import * as TraceEvent from './TraceEvent' | |
| 6 | } |
|
|||
| 7 |
|
||||
| 8 | interface TraceSourceHandler { |
|
|||
| 9 | (source: TraceSource): void; |
|
|||
| 10 | } |
|
|||
| 11 |
|
||||
| 12 | interface Destroyable { |
|
|||
| 13 | destroy(); |
|
|||
| 14 | } |
|
|||
| 15 |
|
6 | |||
| 16 | class Registry { |
|
7 | class Registry { | |
| 17 | static readonly instance = new Registry(); |
|
8 | static readonly instance = new Registry(); | |
| @@ -46,7 +37,7 class Registry { | |||||
| 46 | this._listeners[i].call(null, source); |
|
37 | this._listeners[i].call(null, source); | |
| 47 | } |
|
38 | } | |
| 48 |
|
39 | |||
| 49 |
on(handler: TraceSource |
|
40 | on(handler: (source: TraceSource) => void): IDestroyable { | |
| 50 | argumentNotNull(handler, "handler"); |
|
41 | argumentNotNull(handler, "handler"); | |
| 51 | var me = this; |
|
42 | var me = this; | |
| 52 |
|
43 | |||
| @@ -65,45 +56,18 class Registry { | |||||
| 65 | } |
|
56 | } | |
| 66 | } |
|
57 | } | |
| 67 |
|
58 | |||
| 68 | class TraceSource { |
|
59 | class TraceSource extends Observable<TraceEvent> { | |
| 69 |
|
||||
| 70 | readonly id: any |
|
60 | readonly id: any | |
| 71 |
|
61 | |||
| 72 | // using array will provide faster iteration the with object |
|
|||
| 73 | private _handlers: Array<TraceEventHandler> = new Array<TraceEventHandler>(); |
|
|||
| 74 |
|
||||
| 75 | level: number |
|
62 | level: number | |
| 76 |
|
63 | |||
| 77 | constructor(id: any) { |
|
64 | constructor(id: any) { | |
|
|
65 | super(); | |||
| 78 | this.id = id || new Object(); |
|
66 | this.id = id || new Object(); | |
| 79 | } |
|
67 | } | |
| 80 |
|
68 | |||
| 81 | on(handler: TraceEventHandler): Destroyable { |
|
|||
| 82 | argumentNotNull(handler, "handler"); |
|
|||
| 83 | var me = this; |
|
|||
| 84 | me._handlers.push(handler); |
|
|||
| 85 |
|
||||
| 86 | return { |
|
|||
| 87 | destroy() { |
|
|||
| 88 | me.remove(handler); |
|
|||
| 89 | } |
|
|||
| 90 | } |
|
|||
| 91 | } |
|
|||
| 92 |
|
||||
| 93 | remove(handler: TraceEventHandler): void { |
|
|||
| 94 | let i = this._handlers.indexOf(handler); |
|
|||
| 95 | if (i >= 0) |
|
|||
| 96 | this._handlers.splice(i, 1); |
|
|||
| 97 | } |
|
|||
| 98 |
|
||||
| 99 | protected emit(level: number, arg: any) { |
|
69 | protected emit(level: number, arg: any) { | |
| 100 | this._handlers.forEach(h => { |
|
70 | this._notify(new TraceEvent(this, level, arg)); | |
| 101 | try { |
|
|||
| 102 | h(this, level, arg); |
|
|||
| 103 | } catch (e) { |
|
|||
| 104 | // suppress error in log handlers |
|
|||
| 105 | } |
|
|||
| 106 | }); |
|
|||
| 107 | } |
|
71 | } | |
| 108 |
|
72 | |||
| 109 | isDebugEnabled() { |
|
73 | isDebugEnabled() { | |
| @@ -178,7 +142,7 class TraceSource { | |||||
| 178 | * |
|
142 | * | |
| 179 | * @param handler the handler which will be called for each trace source |
|
143 | * @param handler the handler which will be called for each trace source | |
| 180 | */ |
|
144 | */ | |
| 181 |
static on(handler: TraceSource |
|
145 | static on(handler: (source: TraceSource) => void) { | |
| 182 | return Registry.instance.on(handler); |
|
146 | return Registry.instance.on(handler); | |
| 183 | } |
|
147 | } | |
| 184 |
|
148 | |||
| @@ -1,10 +1,8 | |||||
| 1 | import * as tape from 'tape'; |
|
1 | import * as tape from 'tape'; | |
| 2 | import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; |
|
2 | import * as ActivatableMixin from '@implab/core/components/ActivatableMixin'; | |
| 3 | import { AsyncComponent } from '@implab/core/components/AsyncComponent'; |
|
3 | import { AsyncComponent } from '@implab/core/components/AsyncComponent'; | |
| 4 |
import { IActivationController } from '@implab/core/ |
|
4 | import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces'; | |
| 5 |
import { |
|
5 | import { Cancellation } from '@implab/core/Cancellation'; | |
| 6 | import { ICancellation } from '@implab/core/ICancellation'; |
|
|||
| 7 | import { EmptyCancellation } from '@implab/core/EmptyCancellation'; |
|
|||
| 8 |
|
6 | |||
| 9 | class SimpleActivatable extends ActivatableMixin(AsyncComponent) { |
|
7 | class SimpleActivatable extends ActivatableMixin(AsyncComponent) { | |
| 10 |
|
8 | |||
| @@ -33,20 +31,20 class MockActivationController implement | |||||
| 33 | await component.activate(); |
|
31 | await component.activate(); | |
| 34 | } |
|
32 | } | |
| 35 |
|
33 | |||
| 36 |
async activating(component: IActivatable, ct: ICancellation = |
|
34 | async activating(component: IActivatable, ct: ICancellation = Cancellation.none) { | |
| 37 | if (component != this._active) |
|
35 | if (component != this._active) | |
| 38 | await this.deactivate(); |
|
36 | await this.deactivate(); | |
| 39 | } |
|
37 | } | |
| 40 |
|
38 | |||
| 41 |
async activated(component: IActivatable, ct: ICancellation = |
|
39 | async activated(component: IActivatable, ct: ICancellation = Cancellation.none) { | |
| 42 | this._active = component; |
|
40 | this._active = component; | |
| 43 | } |
|
41 | } | |
| 44 |
|
42 | |||
| 45 |
async deactivating(component: IActivatable, ct: ICancellation = |
|
43 | async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) { | |
| 46 |
|
44 | |||
| 47 | } |
|
45 | } | |
| 48 |
|
46 | |||
| 49 |
async deactivated(component: IActivatable, ct: ICancellation = |
|
47 | async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) { | |
| 50 | if (this._active == component) |
|
48 | if (this._active == component) | |
| 51 | this._active = null; |
|
49 | this._active = null; | |
| 52 | } |
|
50 | } | |
| @@ -1,5 +1,6 | |||||
| 1 | import * as TraceSource from '@implab/core/log/TraceSource' |
|
1 | import * as TraceSource from '@implab/core/log/TraceSource' | |
| 2 | import * as tape from 'tape'; |
|
2 | import * as tape from 'tape'; | |
|
|
3 | import * as ConsoleWriter from '@implab/core/log/writers/ConsoleWriter'; | |||
| 3 |
|
4 | |||
| 4 | const sourceId = 'test/TraceSourceTests'; |
|
5 | const sourceId = 'test/TraceSourceTests'; | |
| 5 |
|
6 | |||
| @@ -8,10 +9,10 tape('trace message', t => { | |||||
| 8 |
|
9 | |||
| 9 | trace.level = TraceSource.DebugLevel; |
|
10 | trace.level = TraceSource.DebugLevel; | |
| 10 |
|
11 | |||
| 11 |
let h = trace.on(( |
|
12 | let h = trace.on((ev) => { | |
| 12 |
t.equal(se |
|
13 | t.equal(ev.source, trace, "sender should be the current trace source"); | |
| 13 |
t.equal(TraceSource.DebugLevel |
|
14 | t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level"); | |
| 14 |
t.equal( |
|
15 | t.equal(ev.arg, "Hello, World!", "The message should be a formatted message"); | |
| 15 |
|
16 | |||
| 16 | t.end(); |
|
17 | t.end(); | |
| 17 | }); |
|
18 | }); | |
| @@ -30,10 +31,10 tape('trace event', t => { | |||||
| 30 | name: "custom event" |
|
31 | name: "custom event" | |
| 31 | }; |
|
32 | }; | |
| 32 |
|
33 | |||
| 33 |
let h = trace.on(( |
|
34 | let h = trace.on((ev) => { | |
| 34 |
t.equal(se |
|
35 | t.equal(ev.source, trace, "sender should be the current trace source"); | |
| 35 |
t.equal(TraceSource.DebugLevel |
|
36 | t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level"); | |
| 36 |
t.equal( |
|
37 | t.equal(ev.arg, event, "The message should be the specified object"); | |
| 37 |
|
38 | |||
| 38 | t.end(); |
|
39 | t.end(); | |
| 39 | }); |
|
40 | }); | |
| @@ -41,4 +42,21 tape('trace event', t => { | |||||
| 41 | trace.traceEvent(TraceSource.DebugLevel, event); |
|
42 | trace.traceEvent(TraceSource.DebugLevel, event); | |
| 42 |
|
43 | |||
| 43 | h.destroy(); |
|
44 | h.destroy(); | |
|
|
45 | }); | |||
|
|
46 | ||||
|
|
47 | tape('console writer', async t => { | |||
|
|
48 | let writer = new ConsoleWriter(); | |||
|
|
49 | ||||
|
|
50 | let trace = TraceSource.get(sourceId); | |||
|
|
51 | trace.level = TraceSource.DebugLevel; | |||
|
|
52 | ||||
|
|
53 | let p = writer.write(trace); | |||
|
|
54 | ||||
|
|
55 | trace.log("Hello, {0}!", 'World'); | |||
|
|
56 | trace.warn("Look at me!"); | |||
|
|
57 | trace.error("DIE!"); | |||
|
|
58 | ||||
|
|
59 | console.log("DONE"); | |||
|
|
60 | ||||
|
|
61 | t.end(); | |||
| 44 | }); No newline at end of file |
|
62 | }); | |
General Comments 0
You need to be logged in to leave comments.
Login now
