-
Notifications
You must be signed in to change notification settings - Fork 513
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 links to make values in tags or log properties clickable #223
Changes from 1 commit
33fcae9
83c257e
1330d0c
f305938
7849922
efd44fd
0901e53
0544a62
858bb10
136c149
73db8e9
a159459
dd795d2
9acf208
68c5d05
13d66cc
6048d0a
185c93e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Signed-off-by: David-Emmanuel Divernois <david-emmanuel.divernois@amadeus.com>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,26 +16,39 @@ | |
|
||
import _uniq from 'lodash/uniq'; | ||
import { getConfigValue } from '../utils/config/get-config'; | ||
import type { Span, Trace } from '../types'; | ||
import type { Span, Trace, Link, KeyValuePair } from '../types'; | ||
|
||
const parameterRegExp = /\$\{([^{}]*)\}/g; | ||
|
||
type ProcessedTemplate = { | ||
parameters: string[], | ||
template: (data: { [parameter: string]: any }) => string, | ||
template: ({ [string]: mixed }) => string, | ||
}; | ||
|
||
type ProcessedLinkPattern = { | ||
object: any, | ||
type: string => boolean, | ||
key: string => boolean, | ||
value: any => boolean, | ||
url: ProcessedTemplate, | ||
text: ProcessedTemplate, | ||
parameters: string[], | ||
}; | ||
|
||
function getParamNames(str) { | ||
const names = []; | ||
const names = new Set(); | ||
str.replace(parameterRegExp, (match, name) => { | ||
names.push(name); | ||
names.add(name); | ||
return match; | ||
}); | ||
return _uniq(names); | ||
return Array.from(names); | ||
} | ||
|
||
function stringSupplant(str, encodeFn: any => string, map) { | ||
return str.replace(parameterRegExp, (_, name) => encodeFn(map[name])); | ||
return str.replace(parameterRegExp, (_, name) => { | ||
const value = map[name]; | ||
return value == null ? '' : encodeFn(value); | ||
}); | ||
} | ||
|
||
export function processTemplate(template: any, encodeFn: any => string): ProcessedTemplate { | ||
|
@@ -82,16 +95,6 @@ export function createTestFunction(entry: any) { | |
|
||
const identity = a => a; | ||
|
||
type ProcessedLinkPattern = { | ||
object: any, | ||
type: string => boolean, | ||
key: string => boolean, | ||
value: any => boolean, | ||
url: ProcessedTemplate, | ||
text: ProcessedTemplate, | ||
parameters: string[], | ||
}; | ||
|
||
export function processLinkPattern(pattern: any): ?ProcessedLinkPattern { | ||
try { | ||
const url = processTemplate(pattern.url, encodeURIComponent); | ||
|
@@ -112,7 +115,7 @@ export function processLinkPattern(pattern: any): ?ProcessedLinkPattern { | |
} | ||
} | ||
|
||
export function getParameterInArray(name: string, array: { key: string, value: any }[]) { | ||
export function getParameterInArray(name: string, array: KeyValuePair[]) { | ||
if (array) { | ||
return array.find(entry => entry.key === name); | ||
} | ||
|
@@ -143,7 +146,7 @@ export function computeLinks( | |
linkPatterns: ProcessedLinkPattern[], | ||
trace: Trace, | ||
spanIndex: number, | ||
items: { key: string, value: any }[], | ||
items: KeyValuePair[], | ||
itemIndex: number | ||
) { | ||
const item = items[itemIndex]; | ||
|
@@ -160,15 +163,15 @@ export function computeLinks( | |
const result = []; | ||
linkPatterns.forEach(pattern => { | ||
if (pattern.type(type) && pattern.key(item.key) && pattern.value(item.value)) { | ||
let parameterValues = {}; | ||
pattern.parameters.every(parameter => { | ||
const parameterValues = {}; | ||
const allParameters = pattern.parameters.every(parameter => { | ||
let entry = getParameterInArray(parameter, items); | ||
if (!entry && !processTags) { | ||
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. I believe ancestors are also being searched when type === 'log', which seems to contradict your comment on the PR description? 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. Yes, ancestors are also being searched when |
||
// do not look in ancestors for process tags because the same object may appear in different places in the hierarchy | ||
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. Given this comment, why are process tags always searched when looking through ancestors? Seems like the only process tags that are skipped are those on the current span. Also, why is it relevant that process tags can be repeated? A process will match the first time it's encountered or it will fail every time it's tested? 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 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. Sorry, having trouble following... Seems like items are only added to the cache in the link getter, at the outer level, and the cache is only checked at that level, too. So, when searching ancestors, previously calculated values that are cached are a non-issue? 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. I will explain in Javascript: // Let's say there is one tag like this:
let myProcessTag = {
key: 'myProcessInfo',
value: 'myProcessValue'
};
// The tag is used in a process:
let myProcess = {
tags: [myProcessTag]
};
// The same process reference is used in multiple spans in the trace hierarchy:
let trace = {
spans: [{
depth: 0
}, {
depth: 1,
tags: [{
key: 'myParentKey',
value: 'parentValue1'
}]
}, {
depth: 2,
process: myProcess
}, {
depth: 1,
tags: [{
key: 'myParentKey',
value: 'parentValue2'
}]
}, {
depth: 2,
process: myProcess
}]
};
// So myProcess is used in trace.spans[2], which is the child of trace.spans[1]
// and myProcess is also used in trace.spans[4], which is the child of trace.spans[3]
// Now, let's suppose that it is allowed for a link of a process tag to refer to tags from ancestors
// and someone defined the configuration like this:
JAEGER_CONFIG = {
"linkPatterns": [{
"key": "myProcessInfo",
"url": "http://example.org/?myProcessValue=#{myProcessInfo}&myParentValue=#{myParentKey}",
"text": "Where will this link go?"
}]
};
// This link on the process tag refers to a tag from an ancestor
// (which is the thing I am not allowing in the current code).
// Then, if we call computeLinks with trace.spans[2] and myProcessTag, we should get:
[{
"url": "http://example.org/?myProcessValue=myProcessValue&myParentValue=parentValue1",
"text": "Where will this link go?"
}]
// Now, if we call computeLinks with trace.spans[4] and the same myProcessTag, we should get:
[{
"url": "http://example.org/?myProcessValue=myProcessValue&myParentValue=parentValue2",
"text": "Where will this link go?"
}]
// These two values are different because the same tag is at two different places in the spans hierarchy,
// and the value for myParentKey is taken
// - in the first case from trace.spans[1].tags[0]
// - in the second case from trace.spans[3].tags[0]
// Now the getLink function returned by createGetLinks uses the tag object as a key in the cache.
// In our case, the tag object is myProcessTag.
// So if we first call the getLink function returned by createGetLinks for trace.spans[2] and myProcessTag :
result = cache.get(myProcessTag); // result is undefined
result = computeLinks(...);
// which means:
result = [{
"url": "http://example.org/?myProcessValue=myProcessValue&myParentValue=parentValue1",
"text": "Where will this link go?"
}];
cache.set(myProcessTag, result); // result is cached
// now if we call the getLink function for trace.spans[4] and myProcessTag :
result = cache.get(myProcessTag); // result is already computed
return [{
"url": "http://example.org/?myProcessValue=myProcessValue&myParentValue=parentValue1",
"text": "Where will this link go?"
}]; // now the return value is wrong for trace.spans[4] I hope this explanation is clearer! 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. Great explanation, I think I've got it... Don't look in ancestors for process tags because the same process object, and therefore tags, can be used for many spans. In this case, searching a span's ancestors could yield different results (if they weren't cached). But, as the cache is keyed on the initial, shared, process tag, all spans with that process would end up getting the same results even if they have different ancestors? 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. Exactly! |
||
// and the cache in getLinks uses that object as a key | ||
entry = getParameterInAncestor(parameter, trace.spans, spanIndex); | ||
} | ||
if (entry && parameterValues) { | ||
if (entry) { | ||
parameterValues[parameter] = entry.value; | ||
return true; | ||
} | ||
|
@@ -177,10 +180,9 @@ export function computeLinks( | |
`Skipping link pattern, missing parameter ${parameter} for key ${item.key} in ${type}.`, | ||
pattern.object | ||
); | ||
parameterValues = null; | ||
return false; | ||
}); | ||
if (parameterValues) { | ||
if (allParameters) { | ||
result.push({ | ||
url: callTemplate(pattern.url, parameterValues), | ||
text: callTemplate(pattern.text, parameterValues), | ||
|
@@ -191,11 +193,8 @@ export function computeLinks( | |
return result; | ||
} | ||
|
||
export function createGetLinks( | ||
linkPatterns: ProcessedLinkPattern[], | ||
cache: WeakMap<{ key: string, value: any }, { url: string, text: string }[]> | ||
) { | ||
return (trace: Trace, spanIndex: number, items: { key: string, value: any }[], itemIndex: number) => { | ||
export function createGetLinks(linkPatterns: ProcessedLinkPattern[], cache: WeakMap<KeyValuePair, Link[]>) { | ||
return (trace: Trace, spanIndex: number, items: KeyValuePair[], itemIndex: number) => { | ||
if (linkPatterns.length === 0) { | ||
return []; | ||
} | ||
|
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.
This file looks great! Awesome.
(Not sure how to add a comment to a file instead of a line...)
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.
Thank you for your encouraging feedback!