@live-change/framework 0.9.171 → 0.9.174

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,24 @@
1
- import KeyBasedExecutionQueues from '../utils/KeyBasedExecutionQueues.js';
2
- import CommandQueue from '../utils/CommandQueue.js';
3
- import SingleEmitQueue from '../utils/SingleEmitQueue.js';
4
- import SplitEmitQueue from '../utils/SplitEmitQueue.js';
1
+ import KeyBasedExecutionQueues from '../utils/KeyBasedExecutionQueues.js'
2
+ import CommandQueue from '../utils/CommandQueue.js'
3
+ import SingleEmitQueue from '../utils/SingleEmitQueue.js'
4
+ import SplitEmitQueue from '../utils/SplitEmitQueue.js'
5
+
6
+ import { context, propagation, trace } from '@opentelemetry/api'
7
+ import { SpanKind } from '@opentelemetry/api'
8
+ const tracer = trace.getTracer('live-change:commandExecutor')
9
+
10
+ import { expandObjectAttributes } from '../utils.js'
11
+
12
+ function spanAttributes(command, service, action) {
13
+ return {
14
+ service: service.name,
15
+ action_name: action.definition.name,
16
+ action_waitForEvents: action.definition.waitForEvents,
17
+ action_timeout: action.definition.timeout,
18
+ action_requestTimeout: action.definition.requestTimeout,
19
+ ...expandObjectAttributes(command, 'command'),
20
+ }
21
+ }
5
22
 
6
23
  async function startCommandExecutor(service, config) {
7
24
  if(!config.runCommands) return
@@ -21,71 +38,124 @@ async function startCommandExecutor(service, config) {
21
38
  Array.isArray(queuedBy) ? (c) => JSON.stringify(queuedBy.map(k => c[k])) :
22
39
  (c) => JSON.stringify(c[queuedBy]))
23
40
  service.commandQueue.addCommandHandler(actionName, async (command) => {
24
- const profileOp = await service.profileLog.begin({
25
- operation: 'queueCommand', commandType: actionName,
26
- commandId: command.id, client: command.client
41
+ if(command._trace) {
42
+ propagation.extract(context.active(), command._trace)
43
+ }
44
+ return tracer.startActiveSpan('queueCommand:'+command.service+'.'+command.type, {
45
+ root: !command._trace,
46
+ attributes: spanAttributes(command, service, action)
47
+ }, async (queueSpan) => {
48
+ console.log("COMMAND TRACE", command._trace)
49
+
50
+ const queueContext = context.active()
51
+
52
+ const profileOp = await service.profileLog.begin({
53
+ operation: 'queueCommand', commandType: actionName,
54
+ commandId: command.id, client: command.client
55
+ })
56
+
57
+ const routine = () => service.profileLog.profile({
58
+ operation: 'runCommand', commandType: actionName,
59
+ commandId: command.id, client: command.client
60
+ }, async () => {
61
+ return tracer.startActiveSpan('handleCommand:'+command.service+'.'+command.type, {
62
+ kind: SpanKind.INTERNAL,
63
+ attributes: spanAttributes(command, service, action)
64
+ }, queueContext, async (handleSpan) => {
65
+
66
+ const _trace = {}
67
+ propagation.inject(context.active(), _trace)
68
+ const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
69
+ const flags = { commandId: command.id, reportFinished, _trace }
70
+ const emit = (!service.app.splitEvents || this.shortEvents)
71
+ ? new SingleEmitQueue(service, flags)
72
+ : new SplitEmitQueue(service, flags)
73
+
74
+ const result = await service.app.assertTime('command ' + action.definition.name,
75
+ action.definition.timeout || 10000,
76
+ async () => action.runCommand(command, (...args) => emit.emit(...args)), command)
77
+
78
+ return tracer.startActiveSpan('handleEvents', {
79
+ kind: SpanKind.INTERNAL,
80
+ attributes: spanAttributes(command, service, action)
81
+ }, queueContext, async (handleEventsSpan) => {
82
+
83
+ if(service.app.shortEvents) {
84
+ const bucket = {}
85
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
86
+ const handlerService = service.app.startedServices[event.service]
87
+ const handler = handlerService.events[event.type]
88
+ handlerService.exentQueue.queue(() => handler.execute(event, bucket))
89
+ }))
90
+ if (action.definition.waitForEvents) await eventsPromise
91
+ } else {
92
+ const events = await emit.commit()
93
+ if (action.definition.waitForEvents)
94
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
95
+ }
96
+ handleEventsSpan.end()
97
+ handleSpan.end()
98
+ queueSpan.end()
99
+ return result
100
+ })
101
+ })
102
+ })
103
+ routine.key = keyFunction(command)
104
+ const promise = service.keyBasedExecutionQueues.queue(routine)
105
+ await service.profileLog.endPromise(profileOp, promise)
106
+ return promise
27
107
  })
28
- const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
29
- const flags = { commandId: command.id, reportFinished }
30
- const emit = (!service.app.splitEvents || this.shortEvents)
31
- ? new SingleEmitQueue(service, flags)
32
- : new SplitEmitQueue(service, flags)
33
- const routine = () => service.profileLog.profile({
108
+ })
109
+ } else {
110
+ service.commandQueue.addCommandHandler(actionName,
111
+ (command) => service.profileLog.profile({
34
112
  operation: 'runCommand', commandType: actionName,
35
113
  commandId: command.id, client: command.client
36
114
  }, async () => {
37
- const result = await service.app.assertTime('command ' + action.definition.name,
38
- action.definition.timeout || 10000,
39
- () => action.runCommand(command, (...args) => emit.emit(...args)), command)
40
- if(service.app.shortEvents) {
41
- const bucket = {}
42
- const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
43
- const handlerService = service.app.startedServices[event.service]
44
- const handler = handlerService.events[event.type]
45
- handlerService.exentQueue.queue(() => handler.execute(event, bucket))
46
- }))
47
- if (action.definition.waitForEvents) await eventsPromise
48
- } else {
49
- const events = await emit.commit()
50
- if (action.definition.waitForEvents)
51
- await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
115
+ if(command._trace) {
116
+ propagation.extract(context.active(), command._trace)
52
117
  }
53
- return result
54
- })
55
- routine.key = keyFunction(command)
56
- const promise = service.keyBasedExecutionQueues.queue(routine)
57
- await service.profileLog.endPromise(profileOp, promise)
58
- return promise
59
- })
60
- } else {
61
- service.commandQueue.addCommandHandler(actionName,
62
- (command) => service.profileLog.profile({
63
- operation: 'runCommand', commandType: actionName,
64
- commandId: command.id, client: command.client
65
- }, async () => {
118
+ return tracer.startActiveSpan('handleCommand:'+command.service+'.'+command.type, {
119
+ kind: SpanKind.INTERNAL,
120
+ attributes: spanAttributes(command, service, action)
121
+ }, async (handleSpan) => {
122
+
123
+ const handleContext = context.active()
124
+
125
+ const _trace = {}
126
+ propagation.inject(context.active(), _trace)
66
127
  const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
67
- const flags = {commandId: command.id, reportFinished}
128
+ const flags = { commandId: command.id, reportFinished, _trace }
68
129
  const emit = (!service.app.splitEvents || this.shortEvents)
69
130
  ? new SingleEmitQueue(service, flags)
70
131
  : new SplitEmitQueue(service, flags)
71
132
  const result = await service.app.assertTime('command ' + action.definition.name,
72
133
  action.definition.timeout || 10000,
73
134
  () => action.runCommand(command, (...args) => emit.emit(...args)), command)
74
- if(service.app.shortEvents) {
75
- const bucket = {}
76
- const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
77
- const handlerService = service.app.startedServices[event.service]
78
- const handler = handlerService.events[event.type]
79
- handlerService.exentQueue.queue(() => handler.execute(event, bucket))
80
- }))
81
- if (action.definition.waitForEvents) await eventsPromise
82
- } else {
83
- const events = await emit.commit()
84
- if (action.definition.waitForEvents)
85
- await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
86
- }
87
- return result
135
+
136
+ return tracer.startActiveSpan('emitEvents', {
137
+ kind: SpanKind.INTERNAL,
138
+ attributes: spanAttributes(command, service, action)
139
+ }, handleContext, async (handleEventsSpan) => {
140
+ if(service.app.shortEvents) {
141
+ const bucket = {}
142
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
143
+ const handlerService = service.app.startedServices[event.service]
144
+ const handler = handlerService.events[event.type]
145
+ handlerService.exentQueue.queue(() => handler.execute(event, bucket))
146
+ }))
147
+ if (action.definition.waitForEvents) await eventsPromise
148
+ } else {
149
+ const events = await emit.commit()
150
+ if (action.definition.waitForEvents)
151
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
152
+ }
153
+ handleEventsSpan.end()
154
+ handleSpan.end()
155
+ return result
156
+ })
88
157
  })
158
+ })
89
159
  )
90
160
 
91
161
  }
@@ -3,6 +3,19 @@ import EventSourcing from '../utils/EventSourcing.js'
3
3
  import Debug from 'debug'
4
4
  const debug = Debug("framework:eventListener")
5
5
 
6
+ import { context, propagation, trace } from '@opentelemetry/api'
7
+ import { SpanKind } from '@opentelemetry/api'
8
+ const tracer = trace.getTracer('live-change:eventListener')
9
+
10
+ import { expandObjectAttributes } from '../utils.js'
11
+
12
+ async function spanAttributes(ev, service) {
13
+ return {
14
+ ...expandObjectAttributes(ev, 'event'),
15
+ service: service.name
16
+ }
17
+ }
18
+
6
19
  async function startEventListener(service, config) {
7
20
  if(!config.handleEvents) return
8
21
 
@@ -19,13 +32,25 @@ async function startEventListener(service, config) {
19
32
  for (let eventName in service.events) {
20
33
  const event = service.events[eventName]
21
34
  service.eventSourcing.addEventHandler(eventName, async (ev, bucket) => {
22
- return await service.profileLog.profile({ operation: "handleEvent", eventName, id: ev.id,
23
- bucketId: bucket.id, triggerId: bucket.triggerId, commandId: bucket.commandId },
24
- () => {
25
- debug("EXECUTING EVENT", ev)
26
- return event.execute(ev, bucket)
27
- }
28
- )
35
+ if(ev._trace) {
36
+ propagation.extract(context.active(), ev._trace)
37
+ }
38
+ return tracer.startActiveSpan('handleEvent:'+service.name+'.'+eventName, {
39
+ kind: SpanKind.INTERNAL,
40
+ attributes: spanAttributes(ev, service)
41
+ }, async (handleSpan) => {
42
+ try {
43
+ return await service.profileLog.profile({ operation: "handleEvent", eventName, id: ev.id,
44
+ bucketId: bucket.id, triggerId: bucket.triggerId, commandId: bucket.commandId },
45
+ () => {
46
+ debug("EXECUTING EVENT", ev)
47
+ return event.execute(ev, bucket)
48
+ }
49
+ )
50
+ } finally {
51
+ handleSpan.end()
52
+ }
53
+ })
29
54
  })
30
55
  service.eventSourcing.onBucketEnd = async (bucket, handledEvents) => {
31
56
  if(bucket.reportFinished && handledEvents.length > 0) {
@@ -3,6 +3,19 @@ import CommandQueue from '../utils/CommandQueue.js'
3
3
  import SingleEmitQueue from '../utils/SingleEmitQueue.js'
4
4
  import SplitEmitQueue from '../utils/SplitEmitQueue.js'
5
5
 
6
+ import { context, propagation, trace } from '@opentelemetry/api'
7
+ import { SpanKind } from '@opentelemetry/api'
8
+ const tracer = trace.getTracer('live-change:triggerExecutor')
9
+
10
+ import { expandObjectAttributes } from '../utils.js'
11
+
12
+ async function spanAttributes(trig, service) {
13
+ return {
14
+ ...expandObjectAttributes(trig, 'trigger'),
15
+ service: service.name
16
+ }
17
+ }
18
+
6
19
  async function startTriggerExecutor(service, config) {
7
20
  if(!config.runCommands) return
8
21
 
@@ -27,7 +40,21 @@ async function startTriggerExecutor(service, config) {
27
40
  await service.dao.request(['database', 'put'], service.databaseName, 'triggerRoutes',
28
41
  { id: triggerName + '=>' + service.name, trigger: triggerName, service: service.name })
29
42
  service.triggerQueue.addCommandHandler(triggerName,
30
- (trig) => Promise.all(triggers.map( trigger => trigger.execute(trig, service) ))
43
+ async (trig) => {
44
+ if(trig._trace) {
45
+ propagation.extract(context.active(), trig._trace)
46
+ }
47
+ return tracer.startActiveSpan('handleTriggerCall:'+service.name+'.'+triggerName, {
48
+ kind: SpanKind.INTERNAL,
49
+ attributes: spanAttributes(trig, service)
50
+ }, async (triggerSpan) => {
51
+ try {
52
+ return await Promise.all(triggers.map( trigger => trigger.execute(trig, service) ))
53
+ } finally {
54
+ triggerSpan.end()
55
+ }
56
+ })
57
+ }
31
58
  )
32
59
  }
33
60
 
@@ -1,57 +1,86 @@
1
1
  import { prepareParameters, processReturn, preFilterParameters } from "./params.js"
2
2
 
3
+ import { context, propagation, trace } from '@opentelemetry/api'
4
+ import { loggingHelpers } from '../utils.js'
5
+ import { SpanKind } from '@opentelemetry/api'
6
+ const tracer = trace.getTracer('live-change:action')
7
+
8
+ import { expandObjectAttributes } from '../utils.js'
9
+
3
10
  class Action {
4
11
 
5
12
  constructor(definition, service) {
6
13
  this.definition = definition
7
14
  this.service = service
15
+ this.loggingHelpers = loggingHelpers(this.service.name, this.service.app.config.clientConfig.version, {
16
+ action: this.definition.name,
17
+ })
8
18
  }
9
19
 
10
- async runCommand(command, emit) {
11
- let parameters = command.parameters
12
- //console.log("PARAMETERS", JSON.stringify(parameters), "DEFN", this.definition.properties)
13
- let preparedParams = await prepareParameters(parameters, this.definition.properties, this.service)
14
- //console.log("PREP PARAMS", preparedParams)
15
-
16
- let resultPromise = (async () => this.definition.execute({
17
- ...parameters,
18
- ...preparedParams
19
- }, {
20
- action: this,
21
- service: this.service,
22
- client: command.client,
23
- command,
24
- trigger: (trigger, data) => this.service.trigger({
25
- causeType: 'action',
26
- cause: command.id,
27
- client: command.client,
28
- ...trigger
29
- }, data),
30
- triggerService: (trigger, data, returnArray = false) => this.service.triggerService({
31
- causeType: 'action',
32
- cause: command.id,
20
+ async runCommand(command, emit, traceContext) {
21
+ return tracer.startActiveSpan('runCommand:'+this.service.name+'.'+this.definition.name, {
22
+ kind: SpanKind.SERVER,
23
+ attributes: {
24
+ ...expandObjectAttributes(command, 'command'),
25
+ service: this.service.name,
26
+ action_name: this.definition.name,
27
+ action_waitForEvents: this.definition.waitForEvents,
28
+ action_timeout: this.definition.timeout,
29
+ action_requestTimeout: this.definition.requestTimeout,
30
+ }
31
+ }, traceContext, async (runSpan) => {
32
+
33
+ let parameters = command.parameters
34
+ //console.log("PARAMETERS", JSON.stringify(parameters), "DEFN", this.definition.properties)
35
+ let preparedParams = await prepareParameters(parameters, this.definition.properties, this.service)
36
+ //console.log("PREP PARAMS", preparedParams)
37
+
38
+ let resultPromise = (async () => this.definition.execute({
39
+ ...parameters,
40
+ ...preparedParams
41
+ }, {
42
+ action: this,
43
+ service: this.service,
33
44
  client: command.client,
34
- ...trigger
35
- }, data, returnArray)
36
- }, emit))()
45
+ command,
46
+ trigger: (trigger, data) => this.service.trigger({
47
+ causeType: 'action',
48
+ cause: command.id,
49
+ client: command.client,
50
+ ...trigger
51
+ }, data),
52
+ triggerService: (trigger, data, returnArray = false) => this.service.triggerService({
53
+ causeType: 'action',
54
+ cause: command.id,
55
+ client: command.client,
56
+ ...trigger
57
+ }, data, returnArray),
58
+ ...this.loggingHelpers
59
+ }, emit))()
37
60
 
38
- resultPromise = resultPromise.then(async result => {
39
- let processedResult = await processReturn(result, this.definition.returns, this.service)
40
- return processedResult
41
- })
42
- resultPromise.catch(error => {
43
- console.error(`Action ${this.definition.name} error `, error.stack || error)
61
+ resultPromise = resultPromise.then(async result => {
62
+ let processedResult = await processReturn(result, this.definition.returns, this.service)
63
+ runSpan.end()
64
+ return processedResult
65
+ })
66
+ resultPromise.catch(error => {
67
+ this.loggingHelpers.error(`Action ${this.definition.name} error `, error.stack || error)
68
+ runSpan.end()
69
+ })
70
+ return resultPromise
44
71
  })
45
- return resultPromise
46
72
  }
47
73
 
48
74
  async callCommand(parameters, clientData) {
49
75
  // if(!clientData.roles) throw new Error("no roles") - roles are not required in frontend
76
+ const _trace = {}
77
+ propagation.inject(context.active(), _trace)
50
78
  const command = {
51
79
  type: this.definition.name,
52
80
  service: this.service.name,
53
81
  client: clientData,
54
- parameters: await preFilterParameters(parameters, this.definition.properties)
82
+ parameters: await preFilterParameters(parameters, this.definition.properties),
83
+ _trace
55
84
  }
56
85
  if(parameters._commandId) command.id = parameters._commandId
57
86
  //console.log("CALL COMMAND", JSON.stringify(command, null, " "))
@@ -1,10 +1,15 @@
1
1
  import { prepareParameters, processReturn } from "./params.js"
2
+ import { loggingHelpers } from '../utils.js'
2
3
 
3
4
  class EventHandler {
4
5
 
5
6
  constructor(definition, service) {
6
7
  this.definition = definition
7
8
  this.service = service
9
+
10
+ this.loggingHelpers = loggingHelpers(service.name, service.app.config.clientConfig.version, {
11
+ eventType: definition.name,
12
+ })
8
13
  }
9
14
 
10
15
  async execute(parameters, bucket) {
@@ -17,7 +22,8 @@ class EventHandler {
17
22
  }, {
18
23
  action: this,
19
24
  service: this.service,
20
- bucket: bucket
25
+ bucket: bucket,
26
+ ...this.loggingHelpers
21
27
  })
22
28
 
23
29
  resultPromise = resultPromise.then(async result => {