| 
									
										
										
										
											2020-11-12 19:29:43 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 	"golang.org/x/time/rate" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 14:37:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/api/frontendlogging" | 
					
						
							| 
									
										
										
										
											2020-11-12 19:29:43 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/web" | 
					
						
							| 
									
										
										
										
											2020-11-12 19:29:43 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var frontendLogger = log.New("frontend") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | type frontendLogMessageHandler func(hs *HTTPServer, c *web.Context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const grafanaJavascriptAgentEndpointPath = "/log-grafana-javascript-agent" | 
					
						
							| 
									
										
										
										
											2020-11-12 19:29:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | func GrafanaJavascriptAgentLogMessageHandler(store *frontendlogging.SourceMapStore) frontendLogMessageHandler { | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 	return func(hs *HTTPServer, c *web.Context) { | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 		event := frontendlogging.FrontendGrafanaJavascriptAgentEvent{} | 
					
						
							|  |  |  | 		if err := web.Bind(c.Req, &event); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 			c.Resp.WriteHeader(http.StatusBadRequest) | 
					
						
							|  |  |  | 			_, err = c.Resp.Write([]byte("bad request data")) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				hs.log.Error("could not write to response", "err", err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Meta object is standard across event types, adding it globally.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 23:40:42 +08:00
										 |  |  | 		if len(event.Logs) > 0 { | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 			for _, logEntry := range event.Logs { | 
					
						
							|  |  |  | 				var ctx = frontendlogging.CtxVector{} | 
					
						
							|  |  |  | 				ctx = event.AddMetaToContext(ctx) | 
					
						
							|  |  |  | 				ctx = append(ctx, "kind", "log", "original_timestamp", logEntry.Timestamp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for k, v := range frontendlogging.KeyValToInterfaceMap(logEntry.KeyValContext()) { | 
					
						
							|  |  |  | 					ctx = append(ctx, k, v) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				switch logEntry.LogLevel { | 
					
						
							|  |  |  | 				case frontendlogging.LogLevelDebug, frontendlogging.LogLevelTrace: | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						ctx = append(ctx, "original_log_level", logEntry.LogLevel) | 
					
						
							|  |  |  | 						frontendLogger.Debug(logEntry.Message, ctx...) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				case frontendlogging.LogLevelError: | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						ctx = append(ctx, "original_log_level", logEntry.LogLevel) | 
					
						
							|  |  |  | 						frontendLogger.Error(logEntry.Message, ctx...) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				case frontendlogging.LogLevelWarning: | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						ctx = append(ctx, "original_log_level", logEntry.LogLevel) | 
					
						
							|  |  |  | 						frontendLogger.Warn(logEntry.Message, ctx...) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						ctx = append(ctx, "original_log_level", logEntry.LogLevel) | 
					
						
							|  |  |  | 						frontendLogger.Info(logEntry.Message, ctx...) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 23:40:42 +08:00
										 |  |  | 		if len(event.Measurements) > 0 { | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 			for _, measurementEntry := range event.Measurements { | 
					
						
							|  |  |  | 				for measurementName, measurementValue := range measurementEntry.Values { | 
					
						
							|  |  |  | 					var ctx = frontendlogging.CtxVector{} | 
					
						
							|  |  |  | 					ctx = event.AddMetaToContext(ctx) | 
					
						
							|  |  |  | 					ctx = append(ctx, measurementName, measurementValue) | 
					
						
							|  |  |  | 					ctx = append(ctx, "kind", "measurement", "original_timestamp", measurementEntry.Timestamp) | 
					
						
							|  |  |  | 					frontendLogger.Info("Measurement: "+measurementEntry.Type, ctx...) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-08-21 23:40:42 +08:00
										 |  |  | 		if len(event.Exceptions) > 0 { | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 			for _, exception := range event.Exceptions { | 
					
						
							|  |  |  | 				var ctx = frontendlogging.CtxVector{} | 
					
						
							|  |  |  | 				ctx = event.AddMetaToContext(ctx) | 
					
						
							|  |  |  | 				exception := exception | 
					
						
							| 
									
										
										
										
											2023-08-10 16:32:12 +08:00
										 |  |  | 				transformedException := frontendlogging.TransformException(c.Req.Context(), &exception, store) | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 				ctx = append(ctx, "kind", "exception", "type", transformedException.Type, "value", transformedException.Value, "stacktrace", transformedException.String()) | 
					
						
							|  |  |  | 				ctx = append(ctx, "original_timestamp", exception.Timestamp) | 
					
						
							|  |  |  | 				frontendLogger.Error(exception.Message(), ctx...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 		c.Resp.WriteHeader(http.StatusAccepted) | 
					
						
							|  |  |  | 		_, err := c.Resp.Write([]byte("OK")) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			hs.log.Error("could not write to response", "err", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // setupFrontendLogHandlers will set up handlers for logs incoming from frontend.
 | 
					
						
							|  |  |  | // handlers are setup even if frontend logging is disabled, but in this case do nothing
 | 
					
						
							|  |  |  | // this is to avoid reporting errors in case config was changes but there are browser
 | 
					
						
							|  |  |  | // sessions still open with older config
 | 
					
						
							|  |  |  | func (hs *HTTPServer) frontendLogEndpoints() web.Handler { | 
					
						
							| 
									
										
										
										
											2023-05-02 17:10:56 +08:00
										 |  |  | 	if !(hs.Cfg.GrafanaJavascriptAgent.Enabled) { | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 		return func(ctx *web.Context) { | 
					
						
							| 
									
										
										
										
											2023-05-02 17:10:56 +08:00
										 |  |  | 			if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == grafanaJavascriptAgentEndpointPath { | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 				ctx.Resp.WriteHeader(http.StatusAccepted) | 
					
						
							|  |  |  | 				_, err := ctx.Resp.Write([]byte("OK")) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					hs.log.Error("could not write to response", "err", err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, hs.pluginStaticRouteResolver, frontendlogging.ReadSourceMapFromFS) | 
					
						
							| 
									
										
										
										
											2023-05-02 17:10:56 +08:00
										 |  |  | 	rateLimiter := rate.NewLimiter(rate.Limit(hs.Cfg.GrafanaJavascriptAgent.EndpointRPS), hs.Cfg.GrafanaJavascriptAgent.EndpointBurst) | 
					
						
							|  |  |  | 	handler := GrafanaJavascriptAgentLogMessageHandler(sourceMapStore) | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return func(ctx *web.Context) { | 
					
						
							| 
									
										
										
										
											2023-05-02 17:10:56 +08:00
										 |  |  | 		if ctx.Req.Method == http.MethodPost && ctx.Req.URL.Path == grafanaJavascriptAgentEndpointPath { | 
					
						
							| 
									
										
										
										
											2022-09-12 18:04:43 +08:00
										 |  |  | 			if !rateLimiter.AllowN(time.Now(), 1) { | 
					
						
							|  |  |  | 				ctx.Resp.WriteHeader(http.StatusTooManyRequests) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			handler(hs, ctx) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-28 15:25:30 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |