mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-22 19:30:43 +01:00
Compare commits
7 Commits
fix/test-j
...
issue_6021
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be8147431d | ||
|
|
57f9774fb1 | ||
|
|
b7abfba3e1 | ||
|
|
64fff60d7e | ||
|
|
dd3b99f19c | ||
|
|
8004b62493 | ||
|
|
631e6eefde |
@@ -20,7 +20,7 @@ export const SlackInitialConfig: Partial<SlackChannel> = {
|
||||
*Summary:* {{ .Annotations.summary }}
|
||||
*Description:* {{ .Annotations.description }}
|
||||
*RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}}
|
||||
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
|
||||
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}}{{ if match "/ai-observability" .Annotations.related_traces -}} View in <{{ .Annotations.related_traces }}|ai traces explorer> {{- else -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end }}{{- end}}
|
||||
|
||||
*Details:*
|
||||
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
|
||||
@@ -137,7 +137,7 @@ export const JsmOpsInitialConfig: Partial<JsmOpsChannel> = {
|
||||
|
||||
{{ end }}{{ if .Annotations.related_logs }}[View related logs]({{ .Annotations.related_logs }})
|
||||
|
||||
{{ end }}{{ if .Annotations.related_traces }}[View related traces]({{ .Annotations.related_traces }})
|
||||
{{ end }}{{ if .Annotations.related_traces }}{{ if match "/ai-observability" .Annotations.related_traces }}[View related AI traces]{{ else }}[View related traces]{{ end }}({{ .Annotations.related_traces }})
|
||||
|
||||
{{ end }}{{ end }}`,
|
||||
priority:
|
||||
@@ -163,7 +163,7 @@ export const IncidentIOInitialConfig: Partial<IncidentIOChannel> = {
|
||||
|
||||
{{ end }}{{ if .Annotations.related_logs }}[View related logs]({{ .Annotations.related_logs }})
|
||||
|
||||
{{ end }}{{ if .Annotations.related_traces }}[View related traces]({{ .Annotations.related_traces }})
|
||||
{{ end }}{{ if .Annotations.related_traces }}{{ if match "/ai-observability" .Annotations.related_traces }}[View related AI traces]{{ else }}[View related traces]{{ end }}({{ .Annotations.related_traces }})
|
||||
|
||||
{{ end }}{{ end }}`,
|
||||
};
|
||||
|
||||
@@ -63,26 +63,6 @@ const EDITED_SPAN_JSON = `{
|
||||
}
|
||||
}`;
|
||||
|
||||
const SPAN_WITH_EXTRA_KEY_JSON = `{
|
||||
"attributes": {
|
||||
"input.value": "What is quantum computing?"
|
||||
},
|
||||
"resource": {
|
||||
"service.name": "llm-gateway"
|
||||
},
|
||||
"demo": {
|
||||
"name": "demo"
|
||||
}
|
||||
}`;
|
||||
|
||||
const EXTRA_KEY_RESULT_SPAN = {
|
||||
attributes: {
|
||||
'input.value': 'What is quantum computing?',
|
||||
[MAPPED_ATTRIBUTE_KEY]: 'What is quantum computing?',
|
||||
},
|
||||
resource: { 'service.name': 'llm-gateway' },
|
||||
};
|
||||
|
||||
const SPAN_INPUT_KEY = LOCALSTORAGE.LLM_ATTRIBUTE_MAPPING_TEST_SPAN;
|
||||
|
||||
describe('TestTab — sample-span flow', () => {
|
||||
@@ -124,47 +104,6 @@ describe('TestTab — sample-span flow', () => {
|
||||
expect(screen.queryByTestId('test-error')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('trims extra top-level keys and sends only the envelope', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
let body: { spans?: { attributes?: Record<string, unknown> }[] } | undefined;
|
||||
server.use(
|
||||
rest.post(TEST_ENDPOINT, async (req, res, ctx) => {
|
||||
body = await req.json();
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json(makeTestResponse([EXTRA_KEY_RESULT_SPAN])),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
render(<LLMObservabilityAttributeMapping />);
|
||||
|
||||
await user.click(screen.getByRole('tab', { name: 'Test' }));
|
||||
const runBtn = await screen.findByTestId('run-test-button');
|
||||
|
||||
await user.clear(screen.getByTestId('monaco'));
|
||||
await user.paste(SPAN_WITH_EXTRA_KEY_JSON);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('monaco')).toHaveValue(SPAN_WITH_EXTRA_KEY_JSON),
|
||||
);
|
||||
expect(screen.queryByTestId('test-input-error')).not.toBeInTheDocument();
|
||||
|
||||
await user.click(runBtn);
|
||||
|
||||
await expect(
|
||||
screen.findByTestId('test-results'),
|
||||
).resolves.toBeInTheDocument();
|
||||
expect(body?.spans?.[0]?.attributes).toStrictEqual({
|
||||
'input.value': 'What is quantum computing?',
|
||||
});
|
||||
expect(screen.getByTestId('test-result-0-attributes')).toHaveTextContent(
|
||||
MAPPED_ATTRIBUTE_KEY,
|
||||
);
|
||||
expect(screen.getByTestId('test-result-0-resource')).toBeInTheDocument();
|
||||
expect(screen.getByText('populated')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('surfaces a backend error and renders no results', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
server.use(
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { parseSpanInput } from '../testPayload';
|
||||
|
||||
describe('parseSpanInput', () => {
|
||||
it('reads the envelope and trims extra top-level keys', () => {
|
||||
const span = parseSpanInput(`{
|
||||
"attributes": { "llm.model_name": "gpt-4o" },
|
||||
"resource": { "service.name": "llm-gateway" },
|
||||
"demo": { "name": "demo" }
|
||||
}`);
|
||||
|
||||
expect(span.attributes).toStrictEqual({ 'llm.model_name': 'gpt-4o' });
|
||||
expect(span.resource).toStrictEqual({ 'service.name': 'llm-gateway' });
|
||||
});
|
||||
|
||||
it('reads a clean envelope', () => {
|
||||
const span = parseSpanInput(`{
|
||||
"attributes": { "llm.model_name": "gpt-4o" },
|
||||
"resource": { "service.name": "llm-gateway" }
|
||||
}`);
|
||||
|
||||
expect(span.attributes).toStrictEqual({ 'llm.model_name': 'gpt-4o' });
|
||||
expect(span.resource).toStrictEqual({ 'service.name': 'llm-gateway' });
|
||||
});
|
||||
|
||||
it('treats an envelope-less object as a bare attribute map', () => {
|
||||
const span = parseSpanInput('{ "llm.model_name": "gpt-4o", "demo": "x" }');
|
||||
|
||||
expect(span.attributes).toStrictEqual({
|
||||
'llm.model_name': 'gpt-4o',
|
||||
demo: 'x',
|
||||
});
|
||||
expect(span.resource).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('drops an envelope key that is not an object', () => {
|
||||
const span = parseSpanInput(
|
||||
'{ "attributes": { "llm.provider": "openai" }, "resource": "oops" }',
|
||||
);
|
||||
|
||||
expect(span.attributes).toStrictEqual({ 'llm.provider': 'openai' });
|
||||
expect(span.resource).toStrictEqual({});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[' ', 'Paste a JSON span object to run the test.'],
|
||||
['{ "a": }', 'Invalid JSON — check for trailing commas or missing quotes.'],
|
||||
['[1, 2]', 'Span must be a JSON object of attribute key-value pairs.'],
|
||||
])('rejects %p', (input, message) => {
|
||||
expect(() => parseSpanInput(input)).toThrow(message);
|
||||
});
|
||||
});
|
||||
@@ -51,9 +51,13 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
// Any other top-level key (a real span carries name, spanId, kind...) is trimmed.
|
||||
function isSpanEnvelope(parsed: Record<string, unknown>): boolean {
|
||||
return isPlainObject(parsed.attributes) || isPlainObject(parsed.resource);
|
||||
const keys = Object.keys(parsed);
|
||||
return (
|
||||
keys.length > 0 &&
|
||||
keys.every((key) => key === 'attributes' || key === 'resource') &&
|
||||
(isPlainObject(parsed.attributes) || isPlainObject(parsed.resource))
|
||||
);
|
||||
}
|
||||
|
||||
export function parseSpanInput(input: string): SpantypesSpanMapperTestSpanDTO {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const editAlertChannelInitialValue = {
|
||||
|
||||
export const slackTitleDefaultValue = `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }} {{- if gt (len .CommonLabels) (len .GroupLabels) -}} {{" "}}( {{- with .CommonLabels.Remove .GroupLabels.Names }} {{- range $index, $label := .SortedPairs -}} {{ if $index }}, {{ end }} {{- $label.Name }}="{{ $label.Value -}}" {{- end }} {{- end -}} ) {{- end }}`;
|
||||
|
||||
export const slackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}} *RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}} *Details:* {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
|
||||
export const slackDescriptionDefaultValue = `{{ range .Alerts -}} *Alert:* {{ .Labels.alertname }}{{ if .Labels.severity }} - {{ .Labels.severity }}{{ end }} *Summary:* {{ .Annotations.summary }} *Description:* {{ .Annotations.description }} *RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}} *RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}}{{ if match "/ai-observability" .Annotations.related_traces -}} View in <{{ .Annotations.related_traces }}|ai traces explorer> {{- else -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end }}{{- end}} *Details:* {{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }} {{ end }} {{ end }}`;
|
||||
|
||||
export const googleChatTitleDefaultValue = `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }}`;
|
||||
|
||||
|
||||
@@ -493,7 +493,7 @@ func appendRelatedLinkButtons(alerts []*types.Alert, bodies []string) {
|
||||
bodies[i] += htmlButton("View Related Logs", string(link))
|
||||
}
|
||||
if link := alerts[i].Annotations[ruletypes.AnnotationRelatedTraces]; link != "" {
|
||||
bodies[i] += htmlButton("View Related Traces", string(link))
|
||||
bodies[i] += htmlButton(ruletypes.RelatedTracesLabel(string(link)), string(link))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,8 @@ func relatedButtons(alert *types.Alert) []button {
|
||||
}
|
||||
}
|
||||
add("View Related Logs", string(alert.Annotations[ruletypes.AnnotationRelatedLogs]))
|
||||
add("View Related Traces", string(alert.Annotations[ruletypes.AnnotationRelatedTraces]))
|
||||
traces := string(alert.Annotations[ruletypes.AnnotationRelatedTraces])
|
||||
add(ruletypes.RelatedTracesLabel(traces), traces)
|
||||
return buttons
|
||||
}
|
||||
|
||||
|
||||
@@ -343,6 +343,16 @@ func TestGoogleChatLinkButtons(t *testing.T) {
|
||||
"View Related Traces": "https://signoz.example/traces",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ai traces link",
|
||||
labels: model.LabelSet{"alertname": "X"},
|
||||
annotations: model.LabelSet{
|
||||
ruletypes.AnnotationRelatedTraces: "https://signoz.example/ai-observability/explorer?q=1",
|
||||
},
|
||||
wantButtons: map[string]string{
|
||||
"View Related AI Traces": "https://signoz.example/ai-observability/explorer?q=1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no links → no buttons",
|
||||
labels: model.LabelSet{"alertname": "X"},
|
||||
|
||||
@@ -214,3 +214,25 @@ func TestNotifyTruncatesLongDescription(t *testing.T) {
|
||||
assert.LessOrEqual(t, len(desc), maxDescriptionLenRunes)
|
||||
assert.Equal(t, '…', desc[len(desc)-1])
|
||||
}
|
||||
|
||||
func TestNotifyRelatedTracesLabel(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
link string
|
||||
want string
|
||||
}{
|
||||
{name: "traces explorer", link: "https://signoz.example/traces-explorer?q=1", want: "[View related traces]"},
|
||||
{name: "ai traces explorer", link: "https://signoz.example/ai-observability/explorer?q=1", want: "[View related AI traces]"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
m := newMockIncidentIO(t)
|
||||
a := alert(true)
|
||||
a.Annotations["related_traces"] = model.LabelValue(c.link)
|
||||
_, err := newNotifier(t, m).Notify(ctx(), a)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, m.lastEvent(t).Description, c.want+"("+c.link+")")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,8 @@ func deepLinks(alerts []*types.Alert) map[string]any {
|
||||
}
|
||||
add("Open in SigNoz", string(a.Labels[ruletypes.LabelRuleSource]))
|
||||
add("View Related Logs", string(a.Annotations[ruletypes.AnnotationRelatedLogs]))
|
||||
add("View Related Traces", string(a.Annotations[ruletypes.AnnotationRelatedTraces]))
|
||||
traces := string(a.Annotations[ruletypes.AnnotationRelatedTraces])
|
||||
add(ruletypes.RelatedTracesLabel(traces), traces)
|
||||
if len(parts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ func buildRelatedLinkActions(alert *types.Alert) []config.SlackAction {
|
||||
actions = append(actions, config.SlackAction{Type: "button", Text: "View Related Logs", URL: string(link)})
|
||||
}
|
||||
if link := alert.Annotations[ruletypes.AnnotationRelatedTraces]; link != "" {
|
||||
actions = append(actions, config.SlackAction{Type: "button", Text: "View Related Traces", URL: string(link)})
|
||||
actions = append(actions, config.SlackAction{Type: "button", Text: ruletypes.RelatedTracesLabel(string(link)), URL: string(link)})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ Description: Request rate exceeded 10k/s`},
|
||||
*Summary:* {{ .Annotations.summary }}
|
||||
*Description:* {{ .Annotations.description }}
|
||||
*RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}}
|
||||
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
|
||||
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}}{{ if match "/ai-observability" .Annotations.related_traces -}} View in <{{ .Annotations.related_traces }}|ai traces explorer> {{- else -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end }}{{- end}}
|
||||
|
||||
*Details:*
|
||||
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}
|
||||
@@ -157,6 +157,31 @@ Description: Request rate exceeded 10k/s`},
|
||||
" "},
|
||||
wantIsDefaultBody: true,
|
||||
},
|
||||
{
|
||||
// AI trace alerts link to the ai-observability explorer; the default
|
||||
// body template labels that link differently from a plain traces link.
|
||||
name: "old template: ai traces link labelled ai traces explorer",
|
||||
alerts: []*types.Alert{
|
||||
createAlert(
|
||||
map[string]string{ruletypes.LabelAlertName: "HighLLMLatency", ruletypes.LabelSeverityName: "warning"},
|
||||
map[string]string{
|
||||
"summary": "LLM latency high",
|
||||
"description": "p99 latency of LLM calls is high",
|
||||
"related_traces": "https://signoz.example.com/ai-observability/explorer?startTime=1&endTime=2",
|
||||
},
|
||||
true,
|
||||
),
|
||||
},
|
||||
input: alertmanagertypes.ExpandRequest{
|
||||
DefaultTitleTemplate: `{{ .CommonLabels.alertname }}`,
|
||||
DefaultBodyTemplate: `{{ range .Alerts -}}
|
||||
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}}{{ if match "/ai-observability" .Annotations.related_traces -}} View in <{{ .Annotations.related_traces }}|ai traces explorer> {{- else -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end }}{{- end}}
|
||||
{{ end }}`,
|
||||
},
|
||||
wantTitle: "HighLLMLatency",
|
||||
wantBody: []string{"*RelatedTraces:* View in <https://signoz.example.com/ai-observability/explorer?startTime=1&endTime=2|ai traces explorer>\n "},
|
||||
wantIsDefaultBody: true,
|
||||
},
|
||||
{
|
||||
// Pod crash loop on multiple pods — body is expanded once per alert
|
||||
// and joined with "\n\n", with the pod name pulled from labels.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"definition": {
|
||||
"name": "gen_ai.llm",
|
||||
"condition": {
|
||||
@@ -114,20 +114,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gen_ai.operation.name",
|
||||
"fieldContext": "attribute",
|
||||
"config": {
|
||||
"sources": [
|
||||
{
|
||||
"key": "llm.request.type",
|
||||
"context": "attribute",
|
||||
"operation": "copy",
|
||||
"priority": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gen_ai.usage.input_tokens",
|
||||
"fieldContext": "attribute",
|
||||
@@ -293,6 +279,12 @@
|
||||
"operation": "copy",
|
||||
"priority": 30
|
||||
},
|
||||
{
|
||||
"key": "ai.response.toolCalls",
|
||||
"context": "attribute",
|
||||
"operation": "copy",
|
||||
"priority": 25
|
||||
},
|
||||
{
|
||||
"key": "ai.response.text",
|
||||
"context": "attribute",
|
||||
@@ -329,7 +321,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gen_ai.response.finish_reason",
|
||||
"name": "gen_ai.response.finish_reasons",
|
||||
"fieldContext": "attribute",
|
||||
"config": {
|
||||
"sources": [
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"definition": {
|
||||
"name": "gen_ai.tool",
|
||||
"condition": {
|
||||
"attributes": [
|
||||
{
|
||||
"value": "tool"
|
||||
"value": "tool.name"
|
||||
},
|
||||
{
|
||||
"value": "ai.toolCall."
|
||||
},
|
||||
{
|
||||
"value": "tool_call_args"
|
||||
},
|
||||
{
|
||||
"value": "tool_response"
|
||||
}
|
||||
],
|
||||
"resource": []
|
||||
|
||||
@@ -311,11 +311,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time) (int, error) {
|
||||
case ruletypes.AlertTypeTraces, ruletypes.AlertTypeAITraces:
|
||||
params := r.prepareParamsForTraces(ctx, ts, smpl.Metric)
|
||||
if len(params) > 0 {
|
||||
explorerPath := "traces-explorer"
|
||||
if r.typ == ruletypes.AlertTypeAITraces {
|
||||
explorerPath = "ai-observability/explorer"
|
||||
}
|
||||
link := r.ExternalURL(explorerPath, params)
|
||||
link := r.ExternalURL(r.typ.TracesExplorerPath(), params)
|
||||
r.logger.InfoContext(ctx, "adding traces link to annotations", slog.String("annotation.link", link))
|
||||
annotations = append(annotations, ruletypes.Label{Name: ruletypes.AnnotationRelatedTraces, Value: link})
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const (
|
||||
|
||||
{{ end }}{{ if .Annotations.related_logs }}[View related logs]({{ .Annotations.related_logs }})
|
||||
|
||||
{{ end }}{{ if .Annotations.related_traces }}[View related traces]({{ .Annotations.related_traces }})
|
||||
{{ end }}{{ if .Annotations.related_traces }}{{ if match "/ai-observability" .Annotations.related_traces }}[View related AI traces]{{ else }}[View related traces]{{ end }}({{ .Annotations.related_traces }})
|
||||
|
||||
{{ end }}{{ end }}`
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ const (
|
||||
|
||||
{{ end }}{{ if .Annotations.related_logs }}[View related logs]({{ .Annotations.related_logs }})
|
||||
|
||||
{{ end }}{{ if .Annotations.related_traces }}[View related traces]({{ .Annotations.related_traces }})
|
||||
{{ end }}{{ if .Annotations.related_traces }}{{ if match "/ai-observability" .Annotations.related_traces }}[View related AI traces]{{ else }}[View related traces]{{ end }}({{ .Annotations.related_traces }})
|
||||
|
||||
{{ end }}{{ end }}`
|
||||
)
|
||||
|
||||
27
pkg/types/ruletypes/related_links.go
Normal file
27
pkg/types/ruletypes/related_links.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package ruletypes
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
DefaultTracesExplorerPath = "traces-explorer"
|
||||
AIObservabilityPathPrefix = "ai-observability"
|
||||
AITracesExplorerPath = AIObservabilityPathPrefix + "/explorer"
|
||||
)
|
||||
|
||||
// TracesExplorerPath is the UI path the alert's related_traces link opens.
|
||||
func (t AlertType) TracesExplorerPath() string {
|
||||
if t == AlertTypeAITraces {
|
||||
return AITracesExplorerPath
|
||||
}
|
||||
return DefaultTracesExplorerPath
|
||||
}
|
||||
|
||||
// RelatedTracesLabel is the button text notifiers show for a related_traces link.
|
||||
// Any link under the AI observability section counts, matching the templates'
|
||||
// `match "/ai-observability"` check.
|
||||
func RelatedTracesLabel(link string) string {
|
||||
if strings.Contains(link, "/"+AIObservabilityPathPrefix) {
|
||||
return "View Related AI Traces"
|
||||
}
|
||||
return "View Related Traces"
|
||||
}
|
||||
Reference in New Issue
Block a user