diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index a808ba1aae7..f3209b89709 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -19,9 +19,10 @@ import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_sr import { GraphiteOptions, GraphiteQuery, GraphiteType, MetricTankRequestMeta } from './types'; import { getRollupNotice, getRuntimeConsolidationNotice } from 'app/plugins/datasource/graphite/meta'; import { getSearchFilterScopedVar } from '../../../features/variables/utils'; -import { Observable, of, OperatorFunction, pipe } from 'rxjs'; +import { Observable, of, OperatorFunction, pipe, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { DEFAULT_GRAPHITE_VERSION } from './versions'; +import { reduceError } from './utils'; export class GraphiteDatasource extends DataSourceApi { basicAuth: string; @@ -645,7 +646,13 @@ export class GraphiteDatasource extends DataSourceApi { + return throwError(reduceError(err)); + }) + ); } buildGraphiteParams(options: any, scopedVars?: ScopedVars): string[] { diff --git a/public/app/plugins/datasource/graphite/utils.test.ts b/public/app/plugins/datasource/graphite/utils.test.ts new file mode 100644 index 00000000000..618cf62e834 --- /dev/null +++ b/public/app/plugins/datasource/graphite/utils.test.ts @@ -0,0 +1,90 @@ +import { reduceError } from './utils'; + +const SAMPLE_500_PAGE = ` +
+

+

Graphite encountered an unexpected error while handling your request.

+

Please contact your site administrator if the problem persists.

+

+
+
+ +
+ +
+
+Traceback (most recent call last):
+  File "/usr/lib/python2.7/dist-packages/django/core/handlers/base.py", line 112, in get_response
+    response = wrapped_callback(request, *callback_args, **callback_kwargs)
+  File "/var/lib/graphite/webapp/graphite/render/views.py", line 125, in renderView
+    seriesList = evaluateTarget(requestContext, target)
+  File "/var/lib/graphite/webapp/graphite/render/evaluator.py", line 10, in evaluateTarget
+    result = evaluateTokens(requestContext, tokens)
+  File "/var/lib/graphite/webapp/graphite/render/evaluator.py", line 21, in evaluateTokens
+    return evaluateTokens(requestContext, tokens.expression)
+  File "/var/lib/graphite/webapp/graphite/render/evaluator.py", line 27, in evaluateTokens
+    func = SeriesFunctions[tokens.call.func]
+KeyError: u'aliasByNodde'
+
+
+
+ +
+`; + +describe('Graphite utils', () => { + it('should reduce HTML based errors', () => { + const error = { + status: 500, + data: { + message: SAMPLE_500_PAGE, + }, + }; + + expect(reduceError(error)).toMatchObject({ + data: { + message: 'Graphite encountered an unexpected error while handling your request. KeyError: aliasByNodde', + }, + }); + }); + + it('should return original error for non-HTML 500 error pages', () => { + const error = { + status: 500, + data: { + message: 'ERROR MESSAGE', + }, + }; + + expect(reduceError(error)).toMatchObject({ + data: { + message: 'ERROR MESSAGE', + }, + }); + }); + + it('should return original error for non 500 errors', () => { + const error = { + status: 400, + data: { + message: 'ERROR MESSAGE', + }, + }; + + expect(reduceError(error)).toMatchObject({ + data: { + message: 'ERROR MESSAGE', + }, + }); + }); + + it('should return original error for errors other than FetchError (not data property)', () => { + const error = { + message: 'ERROR MESSAGE', + }; + + expect(reduceError(error)).toMatchObject({ + message: 'ERROR MESSAGE', + }); + }); +}); diff --git a/public/app/plugins/datasource/graphite/utils.ts b/public/app/plugins/datasource/graphite/utils.ts new file mode 100644 index 00000000000..988161e3573 --- /dev/null +++ b/public/app/plugins/datasource/graphite/utils.ts @@ -0,0 +1,21 @@ +import _ from 'lodash'; + +/** + * Graphite-web before v1.6 returns HTTP 500 with full stack traces in an HTML page + * when a query fails. It results in massive error alerts with HTML tags in the UI. + * This function removes all HTML tags and keeps only the last line from the stack + * trace which should be the most meaningful. + */ +export function reduceError(error: any): any { + if (error && error.status === 500 && error.data?.message?.startsWith('( + error.data.message + .replace(/(<([^>]+)>)/gi, '') + .trim() + .split(/\n/) + )!.replace(/u?&#[^;]+;/g, ''); + error.data.message = `Graphite encountered an unexpected error while handling your request. ${newMessage}`; + } + return error; +}