openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
240 lines (199 loc) • 7.8 kB
JavaScript
import moment from 'moment'
import logger from 'winston'
import * as events from '../model/events'
import { config } from '../config'
config.events = config.get('events')
let normalizationBuffer
if (!config.events) {
// maybe we're using outdated config
config.events = config.get('visualizer')
config.events.normalizationBuffer = config.events.orchestrationTsBufferMillis
}
const enableTSNormalization = config.events.enableTSNormalization != null ? config.events.enableTSNormalization : false
if (enableTSNormalization === true) {
normalizationBuffer = 100
} else {
normalizationBuffer = 0
}
const timestampAsMillis = ts => moment(new Date(ts)).valueOf()
// Determine the difference between baseTS and the earliest timestamp
// present in a collection of routes (buffered for normalization)
function calculateEarliestRouteDiff (baseTS, routes) {
let earliestTS = 0
for (const route of Array.from(routes)) {
const ts = timestampAsMillis(route.request.timestamp)
if (earliestTS < ts) { earliestTS = ts }
}
let tsDiff = baseTS - earliestTS
tsDiff += normalizationBuffer
return tsDiff
}
function determineStatusType (statusCode) {
let status = 'success'
if (statusCode >= 500 && statusCode <= 599) {
status = 'error'
}
return status
}
export function saveEvents (trxEvents, callback) {
const now = new Date()
for (const event of Array.from(trxEvents)) { event.created = now }
// bypass mongoose for quick batch inserts
return events.EventModel.collection.insertMany(trxEvents, err => callback(err))
}
function createRouteEvents (dst, transactionId, channel, route, type, tsAdjustment, autoRetryAttempt) {
if (route != null && route.request != null && route.request.timestamp != null && route.response != null && route.response.timestamp != null) {
let startTS = timestampAsMillis(route.request.timestamp)
let endTS = timestampAsMillis(route.response.timestamp)
if (enableTSNormalization === true) {
startTS += tsAdjustment
endTS += tsAdjustment
}
if (startTS > endTS) { startTS = endTS }
dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: startTS,
type,
event: 'start',
name: route.name,
mediator: route.mediatorURN,
autoRetryAttempt
})
return dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: endTS,
type,
event: 'end',
name: route.name,
mediator: route.mediatorURN,
status: route.response.status,
statusType: determineStatusType(route.response.status),
autoRetryAttempt
})
}
}
function createChannelStartEvent (dst, transactionId, requestTimestamp, channel, autoRetryAttempt) {
return dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: timestampAsMillis(requestTimestamp),
type: 'channel',
event: 'start',
name: channel.name,
autoRetryAttempt
})
}
function createChannelEndEvent (dst, transactionId, requestTimestamp, channel, response, autoRetryAttempt) {
const startTS = timestampAsMillis(requestTimestamp)
let endTS = timestampAsMillis(response.timestamp)
if (endTS < startTS) { endTS = startTS }
return dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: endTS + normalizationBuffer,
type: 'channel',
event: 'end',
name: channel.name,
status: response.status,
statusType: determineStatusType(response.status),
autoRetryAttempt
})
}
function createPrimaryRouteEvents (dst, transactionId, requestTimestamp, channel, routeName, mediatorURN, response, autoRetryAttempt) {
const startTS = timestampAsMillis(requestTimestamp)
dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: startTS,
type: 'primary',
event: 'start',
name: routeName,
mediator: mediatorURN,
autoRetryAttempt
})
let endTS = timestampAsMillis(response.timestamp)
if (endTS < startTS) { endTS = startTS }
return dst.push({
channelID: channel._id,
transactionID: transactionId,
normalizedTimestamp: endTS + normalizationBuffer,
type: 'primary',
event: 'end',
name: routeName,
status: response.status,
statusType: determineStatusType(response.status),
mediator: mediatorURN,
autoRetryAttempt
})
}
function createOrchestrationEvents (dst, transactionId, requestTimestamp, channel, orchestrations) {
let tsDiff
if (requestTimestamp) {
const startTS = timestampAsMillis(requestTimestamp)
tsDiff = calculateEarliestRouteDiff(startTS, orchestrations)
}
return Array.from(orchestrations).map((orch) => createRouteEvents(dst, transactionId, channel, orch, 'orchestration', tsDiff))
}
export function createSecondaryRouteEvents (dst, transactionId, requestTimestamp, channel, routes) {
const startTS = timestampAsMillis(requestTimestamp)
let tsDiff = calculateEarliestRouteDiff(startTS, routes)
const result = []
for (const route of Array.from(routes)) {
let item
createRouteEvents(dst, transactionId, channel, route, 'route', tsDiff)
if (route.orchestrations) {
// find TS difference
tsDiff = calculateEarliestRouteDiff(startTS, route.orchestrations)
item = Array.from(route.orchestrations).map((orch) => createRouteEvents(dst, transactionId, channel, orch, 'orchestration', tsDiff))
}
result.push(item)
}
return result
}
export function createTransactionEvents (dst, transaction, channel) {
function getPrimaryRouteName () {
for (const r of Array.from(channel.routes)) {
if (r.primary) { return r.name }
}
return null
}
const timestamp = (transaction.request != null ? transaction.request.timestamp : undefined) ? transaction.request.timestamp : new Date()
if (transaction.request && transaction.response) {
createPrimaryRouteEvents(dst, transaction._id, timestamp, channel, getPrimaryRouteName(), null, transaction.response)
}
if (transaction.orchestrations) {
createOrchestrationEvents(dst, transaction._id, timestamp, channel, transaction.orchestrations)
}
if (transaction.routes) {
return createSecondaryRouteEvents(dst, transaction._id, timestamp, channel, transaction.routes)
}
}
export async function koaMiddleware (ctx, next) {
const runAsync = method => {
const f = () => method(ctx, (err) => { if (err) { return logger.error(err) } })
return setTimeout(f, 0)
}
runAsync((ctx, done) => {
logger.debug(`Storing channel start event for transaction: ${ctx.transactionId}`)
const trxEvents = []
createChannelStartEvent(trxEvents, ctx.transactionId, ctx.requestTimestamp, ctx.authorisedChannel, ctx.currentAttempt)
return saveEvents(trxEvents, done)
})
await next()
runAsync((ctx, done) => {
logger.debug(`Storing channel end and primary routes events for transaction: ${ctx.transactionId}`)
const trxEvents = []
const mediatorURN = ctx.mediatorResponse != null ? ctx.mediatorResponse['x-mediator-urn'] : undefined
const orchestrations = ctx.mediatorResponse != null ? ctx.mediatorResponse.orchestrations : undefined
if (ctx.primaryRoute != null) {
createPrimaryRouteEvents(trxEvents, ctx.transactionId, ctx.requestTimestamp, ctx.authorisedChannel, ctx.primaryRoute.name, mediatorURN, ctx.response, ctx.currentAttempt)
}
if (orchestrations) {
createOrchestrationEvents(trxEvents, ctx.transactionId, ctx.requestTimestamp, ctx.authorisedChannel, orchestrations, ctx.currentAttempt)
}
createChannelEndEvent(trxEvents, ctx.transactionId, ctx.requestTimestamp, ctx.authorisedChannel, ctx.response, ctx.currentAttempt)
return saveEvents(trxEvents, done)
})
}