Просмотр исходного кода

Merge pull request #10227 from overleaf/spd-td-opentelementry

Add opentelemetry to dev environment

GitOrigin-RevId: 31a8234197337a264412b411429692525793c8b0
Simon Detheridge 4 лет назад
Родитель
Сommit
3d9c8f80f4

+ 4 - 0
libraries/metrics/index.js

@@ -3,6 +3,7 @@ const os = require('os')
 const ExpressCompression = require('compression')
 const ExpressCompression = require('compression')
 const promClient = require('prom-client')
 const promClient = require('prom-client')
 const promWrapper = require('./prom_wrapper')
 const promWrapper = require('./prom_wrapper')
+const tracing = require('./tracing')
 
 
 const DEFAULT_APP_NAME = 'unknown'
 const DEFAULT_APP_NAME = 'unknown'
 
 
@@ -29,6 +30,9 @@ function configure(opts = {}) {
  */
  */
 function initialize(appName, opts = {}) {
 function initialize(appName, opts = {}) {
   appName = appName || DEFAULT_APP_NAME
   appName = appName || DEFAULT_APP_NAME
+  if (tracing.tracingEnabled()) {
+    tracing.initialize(appName)
+  }
   configure({ ...opts, appName })
   configure({ ...opts, appName })
   collectDefaultMetrics({ timeout: 5000, prefix: '' })
   collectDefaultMetrics({ timeout: 5000, prefix: '' })
   promWrapper.setupSweeping()
   promWrapper.setupSweeping()

+ 9 - 0
libraries/metrics/package.json

@@ -8,8 +8,17 @@
   },
   },
   "dependencies": {
   "dependencies": {
     "@google-cloud/debug-agent": "^5.1.2",
     "@google-cloud/debug-agent": "^5.1.2",
+    "@google-cloud/opentelemetry-cloud-trace-exporter": "^1.1.0",
     "@google-cloud/profiler": "^4.1.3",
     "@google-cloud/profiler": "^4.1.3",
     "@google-cloud/trace-agent": "^5.1.1",
     "@google-cloud/trace-agent": "^5.1.1",
+    "@opentelemetry/api": "^1.0.4",
+    "@opentelemetry/auto-instrumentations-node": "^0.28.0",
+    "@opentelemetry/exporter-collector": "^0.25.0",
+    "@opentelemetry/exporter-jaeger": "^1.2.0",
+    "@opentelemetry/exporter-trace-otlp-http": "^0.28.0",
+    "@opentelemetry/resources": "^1.2.0",
+    "@opentelemetry/sdk-node": "^0.28.0",
+    "@opentelemetry/semantic-conventions": "^1.2.0",
     "compression": "^1.7.4",
     "compression": "^1.7.4",
     "prom-client": "^11.1.3",
     "prom-client": "^11.1.3",
     "yn": "^3.1.1"
     "yn": "^3.1.1"

+ 58 - 0
libraries/metrics/tracing.js

@@ -0,0 +1,58 @@
+const opentelemetry = require('@opentelemetry/sdk-node')
+const {
+  getNodeAutoInstrumentations,
+} = require('@opentelemetry/auto-instrumentations-node')
+const {
+  diag,
+  DiagConsoleLogger,
+  DiagLogLevel,
+  trace,
+} = require('@opentelemetry/api')
+const { JaegerExporter } = require('@opentelemetry/exporter-jaeger')
+const { Resource } = require('@opentelemetry/resources')
+const {
+  SemanticResourceAttributes,
+} = require('@opentelemetry/semantic-conventions')
+const GCP = require('@google-cloud/opentelemetry-cloud-trace-exporter')
+
+let tracer
+
+function tracingEnabled() {
+  return process.env.GCP_OPENTELEMETRY || process.env.JAEGER_OPENTELEMETRY
+}
+
+function initialize(appName) {
+  diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO)
+
+  const resource = new Resource({
+    [SemanticResourceAttributes.SERVICE_NAME]: appName,
+    [SemanticResourceAttributes.SERVICE_NAMESPACE]: 'Overleaf',
+    'host.type': 'VM',
+  })
+
+  let exporter
+  if (process.env.GCP_OPENTELEMETRY) {
+    exporter = new GCP.TraceExporter()
+  } else if (process.env.JAEGER_OPENTELEMETRY) {
+    exporter = new JaegerExporter({ host: process.env.JAEGER_HOST || 'jaeger' })
+  } else {
+    return
+  }
+
+  const sdk = new opentelemetry.NodeSDK({
+    traceExporter: exporter,
+    logger: console,
+
+    instrumentations: [getNodeAutoInstrumentations()],
+    resource,
+  })
+
+  tracer = trace.getTracer(appName)
+  sdk.start()
+}
+
+function getTracer() {
+  return tracer
+}
+
+module.exports = { initialize, getTracer, tracingEnabled }

Разница между файлами не показана из-за своего большого размера
+ 1562 - 1163
package-lock.json


+ 1 - 0
services/web/app/src/Features/Project/ProjectController.js

@@ -1289,6 +1289,7 @@ const ProjectController = {
               metadata: { viewport: false },
               metadata: { viewport: false },
               showUpgradePrompt,
               showUpgradePrompt,
               fixedSizeDocument: true,
               fixedSizeDocument: true,
+              useOpenTelemetry: Settings.useOpenTelemetryClient,
             })
             })
             timer.done()
             timer.done()
           }
           }

+ 1 - 1
services/web/app/src/infrastructure/BodyParserWrapper.js

@@ -14,7 +14,7 @@ function isBodyParserError(nextArg) {
 
 
 const wrapBodyParser = method => opts => {
 const wrapBodyParser = method => opts => {
   const middleware = bodyParser[method](opts)
   const middleware = bodyParser[method](opts)
-  return (req, res, next) => {
+  return function bodyParser(req, res, next) {
     middleware(req, res, nextArg => {
     middleware(req, res, nextArg => {
       if (isBodyParserError(nextArg)) {
       if (isBodyParserError(nextArg)) {
         return HttpErrorHandler.handleErrorByStatusCode(
         return HttpErrorHandler.handleErrorByStatusCode(

+ 10 - 9
services/web/app/src/infrastructure/Server.js

@@ -20,7 +20,7 @@ const RedisStore = require('connect-redis')(session)
 const bodyParser = require('./BodyParserWrapper')
 const bodyParser = require('./BodyParserWrapper')
 const methodOverride = require('method-override')
 const methodOverride = require('method-override')
 const cookieParser = require('cookie-parser')
 const cookieParser = require('cookie-parser')
-const bearerToken = require('express-bearer-token')
+const bearerTokenMiddleware = require('express-bearer-token')
 
 
 const passport = require('passport')
 const passport = require('passport')
 const LocalStrategy = require('passport-local').Strategy
 const LocalStrategy = require('passport-local').Strategy
@@ -67,7 +67,7 @@ if (Settings.behindProxy) {
    * X-Original-Forwarded-For. Express expects all proxy IPs to be in a comma
    * X-Original-Forwarded-For. Express expects all proxy IPs to be in a comma
    * separated list in X-Forwarded-For.
    * separated list in X-Forwarded-For.
    */
    */
-  app.use((req, res, next) => {
+  app.use(function getForwardedForHeader(req, res, next) {
     if (
     if (
       req.headers['x-original-forwarded-for'] &&
       req.headers['x-original-forwarded-for'] &&
       req.headers['x-forwarded-for']
       req.headers['x-forwarded-for']
@@ -100,7 +100,7 @@ Object.defineProperty(app.request, 'ip', {
     return ip
     return ip
   },
   },
 })
 })
-app.use(function (req, res, next) {
+app.use(function ignoreAbortedConnections(req, res, next) {
   if (req.destroyed) {
   if (req.destroyed) {
     // Request has been aborted already.
     // Request has been aborted already.
     return
     return
@@ -115,7 +115,7 @@ app.use(function (req, res, next) {
 
 
 if (Settings.exposeHostname) {
 if (Settings.exposeHostname) {
   const HOSTNAME = require('os').hostname()
   const HOSTNAME = require('os').hostname()
-  app.use((req, res, next) => {
+  app.use(function exposeHostname(req, res, next) {
     res.setHeader('X-Served-By', HOSTNAME)
     res.setHeader('X-Served-By', HOSTNAME)
     next()
     next()
   })
   })
@@ -135,7 +135,8 @@ Modules.registerAppMiddleware(app)
 app.use(bodyParser.urlencoded({ extended: true, limit: '2mb' }))
 app.use(bodyParser.urlencoded({ extended: true, limit: '2mb' }))
 app.use(bodyParser.json({ limit: Settings.max_json_request_size }))
 app.use(bodyParser.json({ limit: Settings.max_json_request_size }))
 app.use(methodOverride())
 app.use(methodOverride())
-app.use(bearerToken())
+// add explicit name for telemetry
+app.use(bearerTokenMiddleware())
 
 
 app.use(metrics.http.monitor(logger))
 app.use(metrics.http.monitor(logger))
 
 
@@ -206,7 +207,7 @@ webRouter.use(translations.setLangBasedOnDomainMiddleware)
 
 
 if (Settings.cookieRollingSession) {
 if (Settings.cookieRollingSession) {
   // Measure expiry from last request, not last login
   // Measure expiry from last request, not last login
-  webRouter.use(function (req, res, next) {
+  webRouter.use(function touchSession(req, res, next) {
     if (!req.session.noSessionCallback) {
     if (!req.session.noSessionCallback) {
       req.session.touch()
       req.session.touch()
       if (SessionManager.isUserLoggedIn(req.session)) {
       if (SessionManager.isUserLoggedIn(req.session)) {
@@ -229,7 +230,7 @@ expressLocals(webRouter, privateApiRouter, publicApiRouter)
 
 
 webRouter.use(SessionAutostartMiddleware.invokeCallbackMiddleware)
 webRouter.use(SessionAutostartMiddleware.invokeCallbackMiddleware)
 
 
-webRouter.use(function (req, res, next) {
+webRouter.use(function checkIfSiteClosed(req, res, next) {
   if (Settings.siteIsOpen) {
   if (Settings.siteIsOpen) {
     next()
     next()
   } else if (hasAdminAccess(SessionManager.getSessionUser(req.session))) {
   } else if (hasAdminAccess(SessionManager.getSessionUser(req.session))) {
@@ -239,7 +240,7 @@ webRouter.use(function (req, res, next) {
   }
   }
 })
 })
 
 
-webRouter.use(function (req, res, next) {
+webRouter.use(function checkIfEditorClosed(req, res, next) {
   if (Settings.editorIsOpen) {
   if (Settings.editorIsOpen) {
     next()
     next()
   } else if (req.url.indexOf('/admin') === 0) {
   } else if (req.url.indexOf('/admin') === 0) {
@@ -253,7 +254,7 @@ webRouter.use(AuthenticationController.validateAdmin)
 
 
 // add security headers using Helmet
 // add security headers using Helmet
 const noCacheMiddleware = require('nocache')()
 const noCacheMiddleware = require('nocache')()
-webRouter.use(function (req, res, next) {
+webRouter.use(function addNoCacheHeader(req, res, next) {
   const isLoggedIn = SessionManager.isUserLoggedIn(req.session)
   const isLoggedIn = SessionManager.isUserLoggedIn(req.session)
   const isProjectPage = !!req.path.match('^/project/[a-f0-9]{24}$')
   const isProjectPage = !!req.path.match('^/project/[a-f0-9]{24}$')
   if (isLoggedIn || isProjectPage) {
   if (isLoggedIn || isProjectPage) {

+ 1 - 0
services/web/app/views/project/editor/meta.pug

@@ -27,6 +27,7 @@ meta(name="ol-galileoPromptWords" data-type="string" content=galileoPromptWords)
 meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures)
 meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures)
 meta(name="ol-detachRole" data-type="string" content=detachRole)
 meta(name="ol-detachRole" data-type="string" content=detachRole)
 meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt)
 meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt)
+meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry)
 
 
 - var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {})
 - var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {})
 meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n)
 meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n)

+ 1 - 1
services/web/frontend/js/ide.js

@@ -16,6 +16,7 @@
  * DS207: Consider shorter variations of null checks
  * DS207: Consider shorter variations of null checks
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
  */
+import '../tracing'
 import App from './base'
 import App from './base'
 import FileTreeManager from './ide/file-tree/FileTreeManager'
 import FileTreeManager from './ide/file-tree/FileTreeManager'
 import LoadingManager from './ide/LoadingManager'
 import LoadingManager from './ide/LoadingManager'
@@ -64,7 +65,6 @@ import './features/editor-navigation-toolbar/controllers/editor-navigation-toolb
 import './features/pdf-preview/controllers/pdf-preview-controller'
 import './features/pdf-preview/controllers/pdf-preview-controller'
 import './features/share-project-modal/controllers/react-share-project-modal-controller'
 import './features/share-project-modal/controllers/react-share-project-modal-controller'
 import './features/source-editor/controllers/editor-switch-controller'
 import './features/source-editor/controllers/editor-switch-controller'
-import getMeta from './utils/meta'
 import { cleanupServiceWorker } from './utils/service-worker-cleanup'
 import { cleanupServiceWorker } from './utils/service-worker-cleanup'
 import { reportCM6Perf } from './infrastructure/cm6-performance'
 import { reportCM6Perf } from './infrastructure/cm6-performance'
 
 

+ 37 - 0
services/web/frontend/tracing.js

@@ -0,0 +1,37 @@
+import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
+import { Resource } from '@opentelemetry/resources'
+import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http/build/esnext'
+import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
+import { ZoneContextManager } from '@opentelemetry/context-zone'
+import { registerInstrumentations } from '@opentelemetry/instrumentation'
+import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'
+
+import getMeta from './js/utils/meta'
+
+if (getMeta('ol-useOpenTelemetry')) {
+  const resource = new Resource({
+    [SemanticResourceAttributes.SERVICE_NAME]: 'frontend',
+    [SemanticResourceAttributes.SERVICE_NAMESPACE]: 'Overleaf',
+  })
+
+  const provider = new WebTracerProvider({ resource })
+
+  provider.addSpanProcessor(
+    new SimpleSpanProcessor(
+      new OTLPTraceExporter({
+        url: `https://${window.location.hostname}/otlp/v1/traces`,
+      })
+    )
+  )
+
+  provider.register({
+    // Changing default contextManager to use ZoneContextManager - supports asynchronous operations - optional
+    contextManager: new ZoneContextManager(),
+  })
+
+  // Registering instrumentations
+  registerInstrumentations({
+    instrumentations: [getWebAutoInstrumentations()],
+  })
+}

+ 12 - 0
services/web/package.json

@@ -82,6 +82,18 @@
     "@lezer/common": "^1.0.1",
     "@lezer/common": "^1.0.1",
     "@lezer/highlight": "^1.1.1",
     "@lezer/highlight": "^1.1.1",
     "@lezer/lr": "^1.2.3",
     "@lezer/lr": "^1.2.3",
+    "@opentelemetry/api": "^1.0.4",
+    "@opentelemetry/auto-instrumentations-web": "^0.27.2",
+    "@opentelemetry/context-zone": "^1.2.0",
+    "@opentelemetry/exporter-jaeger": "^1.2.0",
+    "@opentelemetry/exporter-trace-otlp-http": "^0.28.0",
+    "@opentelemetry/instrumentation": "^0.27.0",
+    "@opentelemetry/instrumentation-document-load": "^0.27.1",
+    "@opentelemetry/instrumentation-xml-http-request": "^0.28.0",
+    "@opentelemetry/resources": "^1.2.0",
+    "@opentelemetry/sdk-trace-base": "^1.2.0",
+    "@opentelemetry/sdk-trace-web": "^1.2.0",
+    "@opentelemetry/semantic-conventions": "^1.2.0",
     "@overleaf/logger": "*",
     "@overleaf/logger": "*",
     "@overleaf/metrics": "*",
     "@overleaf/metrics": "*",
     "@overleaf/o-error": "*",
     "@overleaf/o-error": "*",

Некоторые файлы не были показаны из-за большого количества измененных файлов