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