Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
58b566a2
Unverified
Commit
58b566a2
authored
Apr 24, 2020
by
Andrej Ocenas
Committed by
GitHub
Apr 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Tracing: Zipkin datasource (#23829)
parent
800228c1
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
712 additions
and
33 deletions
+712
-33
devenv/docker/blocks/zipkin/docker-compose.yaml
+6
-0
packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.tsx
+2
-0
public/app/features/plugins/built_in_plugins.ts
+3
-0
public/app/plugins/datasource/jaeger/QueryField.tsx
+2
-0
public/app/plugins/datasource/zipkin/ConfigEditor.tsx
+1
-1
public/app/plugins/datasource/zipkin/QueryField.test.tsx
+82
-0
public/app/plugins/datasource/zipkin/QueryField.tsx
+230
-17
public/app/plugins/datasource/zipkin/constants.ts
+1
-0
public/app/plugins/datasource/zipkin/datasource.test.ts
+44
-0
public/app/plugins/datasource/zipkin/datasource.ts
+68
-14
public/app/plugins/datasource/zipkin/types.ts
+21
-0
public/app/plugins/datasource/zipkin/utils/testData.ts
+145
-0
public/app/plugins/datasource/zipkin/utils/transforms.test.ts
+8
-0
public/app/plugins/datasource/zipkin/utils/transforms.ts
+98
-0
scripts/ci-frontend-metrics.sh
+1
-1
No files found.
devenv/docker/blocks/zipkin/docker-compose.yaml
0 → 100644
View file @
58b566a2
# There is no data generator for this so easiest way to get some data here is run this example app
# https://github.com/openzipkin/zipkin-js-example/tree/master/web
zipkin
:
image
:
openzipkin/zipkin:latest
ports
:
-
"
9411:9411"
packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.tsx
View file @
58b566a2
...
...
@@ -52,3 +52,5 @@ export const ButtonCascader: React.FC<ButtonCascaderProps> = props => {
</
RCCascader
>
);
};
ButtonCascader
.
displayName
=
'ButtonCascader'
;
public/app/features/plugins/built_in_plugins.ts
View file @
58b566a2
...
...
@@ -15,6 +15,8 @@ const influxdbPlugin = async () =>
const
lokiPlugin
=
async
()
=>
await
import
(
/* webpackChunkName: "lokiPlugin" */
'app/plugins/datasource/loki/module'
);
const
jaegerPlugin
=
async
()
=>
await
import
(
/* webpackChunkName: "jaegerPlugin" */
'app/plugins/datasource/jaeger/module'
);
const
zipkinPlugin
=
async
()
=>
await
import
(
/* webpackChunkName: "zipkinPlugin" */
'app/plugins/datasource/zipkin/module'
);
const
mixedPlugin
=
async
()
=>
await
import
(
/* webpackChunkName: "mixedPlugin" */
'app/plugins/datasource/mixed/module'
);
const
mysqlPlugin
=
async
()
=>
...
...
@@ -65,6 +67,7 @@ const builtInPlugins: any = {
'app/plugins/datasource/influxdb/module'
:
influxdbPlugin
,
'app/plugins/datasource/loki/module'
:
lokiPlugin
,
'app/plugins/datasource/jaeger/module'
:
jaegerPlugin
,
'app/plugins/datasource/zipkin/module'
:
zipkinPlugin
,
'app/plugins/datasource/mixed/module'
:
mixedPlugin
,
'app/plugins/datasource/mysql/module'
:
mysqlPlugin
,
'app/plugins/datasource/postgres/module'
:
postgresPlugin
,
...
...
public/app/plugins/datasource/jaeger/QueryField.tsx
View file @
58b566a2
...
...
@@ -14,6 +14,7 @@ interface State {
}
function
getLabelFromTrace
(
trace
:
any
):
string
{
// TODO: seems like the spans are not ordered so this may not be actually a root span
const
firstSpan
=
trace
.
spans
&&
trace
.
spans
[
0
];
if
(
firstSpan
)
{
return
`
${
firstSpan
.
operationName
}
[
${
firstSpan
.
duration
}
ms]`
;
...
...
@@ -33,6 +34,7 @@ export class JaegerQueryField extends React.PureComponent<Props, State> {
componentDidMount
()
{
this
.
_isMounted
=
true
;
// We should probably call this periodically to get new services after mount.
this
.
getServices
();
}
...
...
public/app/plugins/datasource/zipkin/ConfigEditor.tsx
View file @
58b566a2
...
...
@@ -7,7 +7,7 @@ export type Props = DataSourcePluginOptionsEditorProps;
export
const
ConfigEditor
:
React
.
FC
<
Props
>
=
({
options
,
onOptionsChange
})
=>
{
return
(
<
DataSourceHttpSettings
defaultUrl=
{
'http://localhost:
3100
'
}
defaultUrl=
{
'http://localhost:
9411
'
}
dataSourceConfig=
{
options
}
showAccessOptions=
{
true
}
onChange=
{
onOptionsChange
}
...
...
public/app/plugins/datasource/zipkin/QueryField.test.tsx
0 → 100644
View file @
58b566a2
import
React
from
'react'
;
import
{
QueryField
,
useLoadOptions
,
useServices
}
from
'./QueryField'
;
import
{
ZipkinDatasource
,
ZipkinQuery
}
from
'./datasource'
;
import
{
shallow
}
from
'enzyme'
;
import
{
ButtonCascader
,
CascaderOption
}
from
'@grafana/ui'
;
import
{
renderHook
,
act
}
from
'@testing-library/react-hooks'
;
describe
(
'QueryField'
,
()
=>
{
it
(
'renders properly'
,
()
=>
{
const
ds
=
{}
as
ZipkinDatasource
;
const
wrapper
=
shallow
(
<
QueryField
history=
{
[]
}
datasource=
{
ds
}
query=
{
{
query
:
'1234'
}
as
ZipkinQuery
}
onRunQuery=
{
()
=>
{}
}
onChange=
{
()
=>
{}
}
/>
);
expect
(
wrapper
.
find
(
ButtonCascader
).
length
).
toBe
(
1
);
expect
(
wrapper
.
find
(
'input'
).
length
).
toBe
(
1
);
expect
(
wrapper
.
find
(
'input'
).
props
().
value
).
toBe
(
'1234'
);
});
});
describe
(
'useServices'
,
()
=>
{
it
(
'returns services from datasource'
,
async
()
=>
{
const
ds
=
{
async
metadataRequest
(
url
:
string
,
params
?:
Record
<
string
,
any
>
):
Promise
<
any
>
{
if
(
url
===
'/api/v2/services'
)
{
return
Promise
.
resolve
([
'service1'
,
'service2'
]);
}
},
}
as
ZipkinDatasource
;
const
{
result
,
waitForNextUpdate
}
=
renderHook
(()
=>
useServices
(
ds
));
await
waitForNextUpdate
();
expect
(
result
.
current
.
value
).
toEqual
([
{
label
:
'service1'
,
value
:
'service1'
,
isLeaf
:
false
},
{
label
:
'service2'
,
value
:
'service2'
,
isLeaf
:
false
},
]);
});
});
describe
(
'useLoadOptions'
,
()
=>
{
it
(
'loads spans and traces'
,
async
()
=>
{
const
ds
=
{
async
metadataRequest
(
url
:
string
,
params
?:
Record
<
string
,
any
>
):
Promise
<
any
>
{
if
(
url
===
'/api/v2/spans'
&&
params
?.
serviceName
===
'service1'
)
{
return
Promise
.
resolve
([
'span1'
,
'span2'
]);
}
console
.
log
({
url
});
if
(
url
===
'/api/v2/traces'
&&
params
?.
serviceName
===
'service1'
&&
params
?.
spanName
===
'span1'
)
{
return
Promise
.
resolve
([[{
name
:
'trace1'
,
duration
:
10
_000
,
traceId
:
'traceId1'
}]]);
}
},
}
as
ZipkinDatasource
;
const
{
result
,
waitForNextUpdate
}
=
renderHook
(()
=>
useLoadOptions
(
ds
));
expect
(
result
.
current
.
allOptions
).
toEqual
({});
act
(()
=>
{
result
.
current
.
onLoadOptions
([{
value
:
'service1'
}
as
CascaderOption
]);
});
await
waitForNextUpdate
();
expect
(
result
.
current
.
allOptions
).
toEqual
({
service1
:
{
span1
:
undefined
,
span2
:
undefined
}
});
act
(()
=>
{
result
.
current
.
onLoadOptions
([{
value
:
'service1'
}
as
CascaderOption
,
{
value
:
'span1'
}
as
CascaderOption
]);
});
await
waitForNextUpdate
();
expect
(
result
.
current
.
allOptions
).
toEqual
({
service1
:
{
span1
:
{
'trace1 [10 ms]'
:
'traceId1'
},
span2
:
undefined
},
});
});
});
public/app/plugins/datasource/zipkin/QueryField.tsx
View file @
58b566a2
import
React
from
'react'
;
import
React
,
{
useCallback
,
useMemo
,
useState
}
from
'react'
;
import
{
ZipkinDatasource
,
ZipkinQuery
}
from
'./datasource'
;
import
{
ExploreQueryFieldProps
}
from
'@grafana/data'
;
import
{
AppEvents
,
ExploreQueryFieldProps
}
from
'@grafana/data'
;
import
{
ButtonCascader
,
CascaderOption
}
from
'@grafana/ui'
;
import
{
useAsyncFn
,
useMount
,
useMountedState
}
from
'react-use'
;
import
{
appEvents
}
from
'../../../core/core'
;
import
{
apiPrefix
}
from
'./constants'
;
import
{
ZipkinSpan
}
from
'./types'
;
import
{
fromPairs
}
from
'lodash'
;
import
{
AsyncState
}
from
'react-use/lib/useAsyncFn'
;
type
Props
=
ExploreQueryFieldProps
<
ZipkinDatasource
,
ZipkinQuery
>
;
export
const
QueryField
=
(
props
:
Props
)
=>
(
<
div
className=
{
'slate-query-field__wrapper'
}
>
<
div
className=
"slate-query-field"
>
<
input
style=
{
{
width
:
'100%'
}
}
value=
{
props
.
query
.
query
||
''
}
onChange=
{
e
=>
props
.
onChange
({
...
props
.
query
,
query
:
e
.
currentTarget
.
value
,
})
export
const
QueryField
=
({
query
,
onChange
,
onRunQuery
,
datasource
}:
Props
)
=>
{
const
serviceOptions
=
useServices
(
datasource
);
const
{
onLoadOptions
,
allOptions
}
=
useLoadOptions
(
datasource
);
const
onSelectTrace
=
useCallback
(
(
values
:
string
[],
selectedOptions
:
CascaderOption
[])
=>
{
if
(
selectedOptions
.
length
===
3
)
{
const
traceID
=
selectedOptions
[
2
].
value
;
onChange
({
...
query
,
query
:
traceID
});
onRunQuery
();
}
},
[
onChange
,
onRunQuery
,
query
]
);
let
cascaderOptions
=
useMapToCascaderOptions
(
serviceOptions
,
allOptions
);
return
(
<>
<
div
className=
"gf-form-inline gf-form-inline--nowrap"
>
<
div
className=
"gf-form flex-shrink-0"
>
<
ButtonCascader
options=
{
cascaderOptions
}
onChange=
{
onSelectTrace
}
loadData=
{
onLoadOptions
}
>
Traces
</
ButtonCascader
>
</
div
>
<
div
className=
"gf-form gf-form--grow flex-shrink-1"
>
<
div
className=
{
'slate-query-field__wrapper'
}
>
<
div
className=
"slate-query-field"
>
<
input
style=
{
{
width
:
'100%'
}
}
value=
{
query
.
query
||
''
}
onChange=
{
e
=>
onChange
({
...
query
,
query
:
e
.
currentTarget
.
value
,
})
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
</>
);
};
// Exported for tests
export
function
useServices
(
datasource
:
ZipkinDatasource
):
AsyncState
<
CascaderOption
[]
>
{
const
url
=
`
${
apiPrefix
}
/services`
;
const
[
servicesOptions
,
fetch
]
=
useAsyncFn
(
async
():
Promise
<
CascaderOption
[]
>
=>
{
try
{
const
services
:
string
[]
|
null
=
await
datasource
.
metadataRequest
(
url
);
if
(
services
)
{
return
services
.
sort
().
map
(
service
=>
({
label
:
service
,
value
:
service
,
isLeaf
:
false
,
}));
}
return
[];
}
catch
(
error
)
{
appEvents
.
emit
(
AppEvents
.
alertError
,
[
'Failed to load services from Zipkin'
,
error
]);
throw
error
;
}
},
[
datasource
]);
useMount
(()
=>
{
// We should probably call this periodically to get new services after mount.
fetch
();
});
return
servicesOptions
;
}
type
OptionsState
=
{
[
serviceName
:
string
]:
{
[
spanName
:
string
]:
{
[
traceId
:
string
]:
string
;
};
};
};
// Exported for tests
export
function
useLoadOptions
(
datasource
:
ZipkinDatasource
)
{
const
isMounted
=
useMountedState
();
const
[
allOptions
,
setAllOptions
]
=
useState
({}
as
OptionsState
);
const
[,
fetchSpans
]
=
useAsyncFn
(
async
function
findSpans
(
service
:
string
):
Promise
<
void
>
{
const
url
=
`
${
apiPrefix
}
/spans`
;
try
{
// The response of this should have been full ZipkinSpan objects based on API docs but is just list
// of span names.
// TODO: check if this is some issue of version used or something else
const
response
:
string
[]
=
await
datasource
.
metadataRequest
(
url
,
{
serviceName
:
service
});
if
(
isMounted
())
{
setAllOptions
(
state
=>
{
const
spanOptions
=
fromPairs
(
response
.
map
((
span
:
string
)
=>
[
span
,
undefined
]));
return
{
...
state
,
[
service
]:
spanOptions
as
any
,
};
});
}
/>
</
div
>
</
div
>
);
}
catch
(
error
)
{
appEvents
.
emit
(
AppEvents
.
alertError
,
[
'Failed to load spans from Zipkin'
,
error
]);
throw
error
;
}
},
[
datasource
,
allOptions
]
);
const
[,
fetchTraces
]
=
useAsyncFn
(
async
function
findTraces
(
serviceName
:
string
,
spanName
:
string
):
Promise
<
void
>
{
const
url
=
`
${
apiPrefix
}
/traces`
;
const
search
=
{
serviceName
,
spanName
,
// See other params and default here https://zipkin.io/zipkin-api/#/default/get_traces
};
try
{
// This should return just root traces as there isn't any nesting
const
traces
:
ZipkinSpan
[][]
=
await
datasource
.
metadataRequest
(
url
,
search
);
if
(
isMounted
())
{
const
newTraces
=
traces
.
length
?
fromPairs
(
traces
.
map
(
trace
=>
{
const
rootSpan
=
trace
.
find
(
span
=>
!
span
.
parentId
);
return
[
`
${
rootSpan
.
name
}
[
${
Math
.
floor
(
rootSpan
.
duration
/
1000
)}
ms]`
,
rootSpan
.
traceId
];
})
)
:
noTracesOptions
;
setAllOptions
(
state
=>
{
const
spans
=
state
[
serviceName
];
return
{
...
state
,
[
serviceName
]:
{
...
spans
,
[
spanName
]:
newTraces
,
},
};
});
}
}
catch
(
error
)
{
appEvents
.
emit
(
AppEvents
.
alertError
,
[
'Failed to load spans from Zipkin'
,
error
]);
throw
error
;
}
},
[
datasource
]
);
const
onLoadOptions
=
useCallback
(
(
selectedOptions
:
CascaderOption
[])
=>
{
const
service
=
selectedOptions
[
0
].
value
;
if
(
selectedOptions
.
length
===
1
)
{
fetchSpans
(
service
);
}
else
if
(
selectedOptions
.
length
===
2
)
{
const
spanName
=
selectedOptions
[
1
].
value
;
fetchTraces
(
service
,
spanName
);
}
},
[
fetchSpans
,
fetchTraces
]
);
return
{
onLoadOptions
,
allOptions
,
};
}
function
useMapToCascaderOptions
(
services
:
AsyncState
<
CascaderOption
[]
>
,
allOptions
:
OptionsState
)
{
return
useMemo
(()
=>
{
let
cascaderOptions
:
CascaderOption
[];
if
(
services
.
value
&&
services
.
value
.
length
)
{
cascaderOptions
=
services
.
value
.
map
(
services
=>
{
return
{
...
services
,
children
:
allOptions
[
services
.
value
]
&&
Object
.
keys
(
allOptions
[
services
.
value
]).
map
(
spanName
=>
{
return
{
label
:
spanName
,
value
:
spanName
,
isLeaf
:
false
,
children
:
allOptions
[
services
.
value
][
spanName
]
&&
Object
.
keys
(
allOptions
[
services
.
value
][
spanName
]).
map
(
traceName
=>
{
return
{
label
:
traceName
,
value
:
allOptions
[
services
.
value
][
spanName
][
traceName
],
};
}),
};
}),
};
});
}
else
if
(
services
.
value
&&
!
services
.
value
.
length
)
{
cascaderOptions
=
noTracesFoundOptions
;
}
return
cascaderOptions
;
},
[
services
,
allOptions
]);
}
const
NO_TRACES_KEY
=
'__NO_TRACES__'
;
const
noTracesFoundOptions
=
[
{
label
:
'No traces found'
,
value
:
'no_traces'
,
isLeaf
:
true
,
// Cannot be disabled because then cascader shows 'loading' for some reason.
// disabled: true,
},
];
const
noTracesOptions
=
{
'[No traces in time range]'
:
NO_TRACES_KEY
,
};
public/app/plugins/datasource/zipkin/constants.ts
0 → 100644
View file @
58b566a2
export
const
apiPrefix
=
'/api/v2'
;
public/app/plugins/datasource/zipkin/datasource.test.ts
0 → 100644
View file @
58b566a2
import
{
ZipkinDatasource
,
ZipkinQuery
}
from
'./datasource'
;
import
{
DataQueryRequest
,
DataSourceInstanceSettings
}
from
'@grafana/data'
;
import
{
BackendSrv
,
BackendSrvRequest
,
setBackendSrv
}
from
'@grafana/runtime'
;
import
{
jaegerTrace
,
zipkinResponse
}
from
'./utils/testData'
;
describe
(
'ZipkinDatasource'
,
()
=>
{
describe
(
'query'
,
()
=>
{
it
(
'runs query'
,
async
()
=>
{
setupBackendSrv
({
url
:
'/api/datasources/proxy/1/api/v2/trace/12345'
,
response
:
zipkinResponse
});
const
ds
=
new
ZipkinDatasource
(
defaultSettings
);
const
response
=
await
ds
.
query
({
targets
:
[{
query
:
'12345'
}]
}
as
DataQueryRequest
<
ZipkinQuery
>
).
toPromise
();
expect
(
response
.
data
[
0
].
fields
[
0
].
values
.
get
(
0
)).
toEqual
(
jaegerTrace
);
});
});
describe
(
'metadataRequest'
,
()
=>
{
it
(
'runs query'
,
async
()
=>
{
setupBackendSrv
({
url
:
'/api/datasources/proxy/1/api/v2/services'
,
response
:
[
'service 1'
,
'service 2'
]
});
const
ds
=
new
ZipkinDatasource
(
defaultSettings
);
const
response
=
await
ds
.
metadataRequest
(
'/api/v2/services'
);
expect
(
response
).
toEqual
([
'service 1'
,
'service 2'
]);
});
});
});
function
setupBackendSrv
<
T
>
({
url
,
response
}:
{
url
:
string
;
response
:
T
}):
void
{
setBackendSrv
({
datasourceRequest
(
options
:
BackendSrvRequest
):
Promise
<
any
>
{
if
(
options
.
url
===
url
)
{
return
Promise
.
resolve
({
data
:
response
});
}
throw
new
Error
(
`Unexpected url
${
options
.
url
}
`
);
},
}
as
BackendSrv
);
}
const
defaultSettings
:
DataSourceInstanceSettings
=
{
id
:
1
,
uid
:
'1'
,
type
:
'tracing'
,
name
:
'zipkin'
,
meta
:
{}
as
any
,
jsonData
:
{},
};
public/app/plugins/datasource/zipkin/datasource.ts
View file @
58b566a2
...
...
@@ -5,34 +5,88 @@ import {
DataQueryRequest
,
DataQueryResponse
,
DataQuery
,
FieldType
,
}
from
'@grafana/data'
;
import
{
Observable
,
of
}
from
'rxjs'
;
import
{
from
,
Observable
,
of
}
from
'rxjs'
;
import
{
DatasourceRequestOptions
}
from
'../../../core/services/backend_srv'
;
import
{
serializeParams
}
from
'../../../core/utils/fetch'
;
import
{
getBackendSrv
}
from
'@grafana/runtime'
;
import
{
map
}
from
'rxjs/operators'
;
import
{
apiPrefix
}
from
'./constants'
;
import
{
ZipkinSpan
}
from
'./types'
;
import
{
transformResponse
}
from
'./utils/transforms'
;
export
type
ZipkinQuery
=
{
// At the moment this should be simply the trace ID to get
query
:
string
;
}
&
DataQuery
;
export
class
ZipkinDatasource
extends
DataSourceApi
<
ZipkinQuery
>
{
constructor
(
instanceSettings
:
DataSourceInstanceSettings
)
{
constructor
(
private
instanceSettings
:
DataSourceInstanceSettings
)
{
super
(
instanceSettings
);
}
query
(
options
:
DataQueryRequest
<
ZipkinQuery
>
):
Observable
<
DataQueryResponse
>
{
return
of
({
data
:
[
new
MutableDataFrame
({
fields
:
[
{
name
:
'url'
,
values
:
[],
},
],
}),
],
});
const
traceId
=
options
.
targets
[
0
]?.
query
;
if
(
traceId
)
{
return
this
.
request
<
ZipkinSpan
[]
>
(
`
${
apiPrefix
}
/trace/
${
traceId
}
`
).
pipe
(
map
(
responseToDataQueryResponse
));
}
else
{
return
of
(
emptyDataQueryResponse
);
}
}
async
metadataRequest
(
url
:
string
,
params
?:
Record
<
string
,
any
>
):
Promise
<
any
>
{
const
res
=
await
this
.
request
(
url
,
params
,
{
silent
:
true
}).
toPromise
();
return
res
.
data
;
}
async
testDatasource
():
Promise
<
any
>
{
await
this
.
metadataRequest
(
`
${
apiPrefix
}
/services`
);
return
true
;
}
private
request
<
T
=
any
>
(
apiUrl
:
string
,
data
?:
any
,
options
?:
DatasourceRequestOptions
):
Observable
<
{
data
:
T
}
>
{
// Hack for proxying metadata requests
const
baseUrl
=
`/api/datasources/proxy/
${
this
.
instanceSettings
.
id
}
`
;
const
params
=
data
?
serializeParams
(
data
)
:
''
;
const
url
=
`
${
baseUrl
}${
apiUrl
}${
params
.
length
?
`?
${
params
}
`
:
''
}
`
;
const
req
=
{
...
options
,
url
,
};
return
from
(
getBackendSrv
().
datasourceRequest
(
req
));
}
}
function
responseToDataQueryResponse
(
response
:
{
data
:
ZipkinSpan
[]
}):
DataQueryResponse
{
return
{
data
:
[
new
MutableDataFrame
({
fields
:
[
{
name
:
'trace'
,
type
:
FieldType
.
trace
,
// There is probably better mapping than just putting everything in as a single value but that's how
// we do it with jaeger and is the simplest right now.
values
:
response
?.
data
?
[
transformResponse
(
response
?.
data
)]
:
[],
},
],
}),
],
};
}
const
emptyDataQueryResponse
=
{
data
:
[
new
MutableDataFrame
({
fields
:
[
{
name
:
'trace'
,
type
:
FieldType
.
trace
,
values
:
[],
},
],
}),
],
};
public/app/plugins/datasource/zipkin/types.ts
0 → 100644
View file @
58b566a2
export
type
ZipkinSpan
=
{
traceId
:
string
;
parentId
?:
string
;
name
:
string
;
id
:
string
;
timestamp
:
number
;
duration
:
number
;
localEndpoint
:
{
serviceName
:
string
;
ipv4
:
string
;
port
?:
number
;
};
annotations
?:
ZipkinAnnotation
[];
tags
?:
{
[
key
:
string
]:
string
};
kind
?:
'CLIENT'
|
'SERVER'
|
'PRODUCER'
|
'CONSUMER'
;
};
export
type
ZipkinAnnotation
=
{
timestamp
:
number
;
value
:
string
;
};
public/app/plugins/datasource/zipkin/utils/testData.ts
0 → 100644
View file @
58b566a2
import
{
SpanData
,
TraceData
}
from
'@jaegertracing/jaeger-ui-components'
;
import
{
ZipkinSpan
}
from
'../types'
;
export
const
zipkinResponse
:
ZipkinSpan
[]
=
[
{
traceId
:
'trace_id'
,
name
:
'span 1'
,
id
:
'span 1 id'
,
timestamp
:
1
,
duration
:
10
,
localEndpoint
:
{
serviceName
:
'service 1'
,
ipv4
:
'1.0.0.1'
,
port
:
42
,
},
annotations
:
[
{
timestamp
:
2
,
value
:
'annotation text'
,
},
{
timestamp
:
6
,
value
:
'annotation text 3'
,
},
],
tags
:
{
tag1
:
'val1'
,
tag2
:
'val2'
,
},
kind
:
'CLIENT'
,
},
{
traceId
:
'trace_id'
,
parentId
:
'span 1 id'
,
name
:
'span 2'
,
id
:
'span 2 id'
,
timestamp
:
4
,
duration
:
5
,
localEndpoint
:
{
serviceName
:
'service 2'
,
ipv4
:
'1.0.0.1'
,
},
tags
:
{
error
:
'404'
,
},
},
];
export
const
jaegerTrace
:
TraceData
&
{
spans
:
SpanData
[]
}
=
{
processes
:
{
'service 1'
:
{
serviceName
:
'service 1'
,
tags
:
[
{
key
:
'ipv4'
,
type
:
'string'
,
value
:
'1.0.0.1'
,
},
{
key
:
'port'
,
type
:
'number'
,
value
:
42
,
},
],
},
'service 2'
:
{
serviceName
:
'service 2'
,
tags
:
[
{
key
:
'ipv4'
,
type
:
'string'
,
value
:
'1.0.0.1'
,
},
],
},
},
traceID
:
'trace_id'
,
warnings
:
null
,
spans
:
[
{
duration
:
10
,
flags
:
1
,
logs
:
[
{
timestamp
:
2
,
fields
:
[{
key
:
'annotation'
,
type
:
'string'
,
value
:
'annotation text'
}],
},
{
timestamp
:
6
,
fields
:
[{
key
:
'annotation'
,
type
:
'string'
,
value
:
'annotation text 3'
}],
},
],
operationName
:
'span 1'
,
processID
:
'service 1'
,
startTime
:
1
,
spanID
:
'span 1 id'
,
traceID
:
'trace_id'
,
warnings
:
null
as
any
,
tags
:
[
{
key
:
'kind'
,
type
:
'string'
,
value
:
'CLIENT'
,
},
{
key
:
'tag1'
,
type
:
'string'
,
value
:
'val1'
,
},
{
key
:
'tag2'
,
type
:
'string'
,
value
:
'val2'
,
},
],
references
:
[],
},
{
duration
:
5
,
flags
:
1
,
logs
:
[],
operationName
:
'span 2'
,
processID
:
'service 2'
,
startTime
:
4
,
spanID
:
'span 2 id'
,
traceID
:
'trace_id'
,
warnings
:
null
as
any
,
tags
:
[
{
key
:
'error'
,
type
:
'bool'
,
value
:
true
,
},
],
references
:
[
{
refType
:
'CHILD_OF'
,
spanID
:
'span 1 id'
,
traceID
:
'trace_id'
,
},
],
},
],
};
public/app/plugins/datasource/zipkin/utils/transforms.test.ts
0 → 100644
View file @
58b566a2
import
{
transformResponse
}
from
'./transforms'
;
import
{
jaegerTrace
,
zipkinResponse
}
from
'./testData'
;
describe
(
'transformResponse'
,
()
=>
{
it
(
'transforms response'
,
()
=>
{
expect
(
transformResponse
(
zipkinResponse
)).
toEqual
(
jaegerTrace
);
});
});
public/app/plugins/datasource/zipkin/utils/transforms.ts
0 → 100644
View file @
58b566a2
import
{
identity
}
from
'lodash'
;
import
{
keyBy
}
from
'lodash'
;
import
{
ZipkinAnnotation
,
ZipkinSpan
}
from
'../types'
;
import
{
KeyValuePair
,
Log
,
Process
,
SpanData
,
TraceData
}
from
'@jaegertracing/jaeger-ui-components'
;
/**
* Transforms response to format similar to Jaegers as we use Jaeger ui on the frontend.
*/
export
function
transformResponse
(
zSpans
:
ZipkinSpan
[]):
TraceData
&
{
spans
:
SpanData
[]
}
{
return
{
processes
:
gatherProcesses
(
zSpans
),
traceID
:
zSpans
[
0
].
traceId
,
spans
:
zSpans
.
map
(
transformSpan
),
warnings
:
null
,
};
}
function
transformSpan
(
span
:
ZipkinSpan
):
SpanData
{
const
jaegerSpan
:
SpanData
=
{
duration
:
span
.
duration
,
// TODO: not sure what this is
flags
:
1
,
logs
:
span
.
annotations
?.
map
(
transformAnnotation
)
??
[],
operationName
:
span
.
name
,
processID
:
span
.
localEndpoint
.
serviceName
,
startTime
:
span
.
timestamp
,
spanID
:
span
.
id
,
traceID
:
span
.
traceId
,
warnings
:
null
as
any
,
tags
:
Object
.
keys
(
span
.
tags
||
{}).
map
(
key
=>
{
// If tag is error we remap it to simple boolean so that the Jaeger ui will show an error icon.
return
{
key
,
type
:
key
===
'error'
?
'bool'
:
'string'
,
value
:
key
===
'error'
?
true
:
span
.
tags
!
[
key
],
};
}),
references
:
span
.
parentId
?
[
{
refType
:
'CHILD_OF'
,
spanID
:
span
.
parentId
,
traceID
:
span
.
traceId
,
},
]
:
[],
};
if
(
span
.
kind
)
{
jaegerSpan
.
tags
=
[
{
key
:
'kind'
,
type
:
'string'
,
value
:
span
.
kind
,
},
...
jaegerSpan
.
tags
,
];
}
return
jaegerSpan
;
}
/**
* Maps annotations as a Jaeger log as that seems to be the closest thing.
* See https://zipkin.io/zipkin-api/#/default/get_trace__traceId_
*/
function
transformAnnotation
(
annotation
:
ZipkinAnnotation
):
Log
{
return
{
timestamp
:
annotation
.
timestamp
,
fields
:
[
{
key
:
'annotation'
,
type
:
'string'
,
value
:
annotation
.
value
,
},
],
};
}
function
gatherProcesses
(
zSpans
:
ZipkinSpan
[]):
Record
<
string
,
Process
>
{
const
processes
=
zSpans
.
map
(
span
=>
({
serviceName
:
span
.
localEndpoint
.
serviceName
,
tags
:
[
{
key
:
'ipv4'
,
type
:
'string'
,
value
:
span
.
localEndpoint
.
ipv4
,
},
span
.
localEndpoint
.
port
?
{
key
:
'port'
,
type
:
'number'
,
value
:
span
.
localEndpoint
.
port
,
}
:
undefined
,
].
filter
(
identity
)
as
KeyValuePair
[],
}));
return
keyBy
(
processes
,
'serviceName'
);
}
scripts/ci-frontend-metrics.sh
View file @
58b566a2
...
...
@@ -4,7 +4,7 @@ echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT
=
7
91
ERROR_COUNT_LIMIT
=
7
88
DIRECTIVES_LIMIT
=
172
CONTROLLERS_LIMIT
=
139
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment