Google Cloud Run
Prerequisites
- A Google Cloud project with Cloud Run and Artifact Registry enabled
- gcloud CLI authenticated and configured
- A containerised application (Dockerfile)
- Node.js 18+ for the Node section; .NET 6+ for the .NET section
Features Supported
Capability | Node.js | .NET |
---|---|---|
Traces | ✅ | ✅ |
Metrics | ✅ | ✅ |
App Logs | ✅ | ✅ |
Custom Logs | ✅ | ✅ |
Profiling | ✅ | ✖ |
1 Install the APM package
1npm install @middleware.io/node-apm --save
Ensure this package is listed in package.json so your container build installs it.
2 Initialise the tracker before your app code
You must initialise the tracker before loading any other modules so auto‑instrumentation can hook framework/runtime internals.
- JavaScript (CommonJS) via preload file +
--require
Create instrumentation.js:Start your app with Node’s preload:1// instrumentation.js 2const tracker = require('@middleware.io/node-apm'); 3 4tracker.track({ 5 serviceName: '<MW_SERVICE_NAME>', 6 accessToken: '<MW_API_KEY>', 7 target: 'https://<MW_UID>.middleware.io:443', 8 // Optional extras 9 projectName: '<MW_PROJECT_NAME>', 10});
If your app is ESM‑only, --require won’t work. Use the ESM approach below.1node --require ./instrumentation.js app.js
- JavaScript (ESM) via --import Create instrumentation.mjs:Run using Node’s ESM import hook (Node 18+):
1// instrumentation.mjs 2import * as tracker from '@middleware.io/node-apm'; 3 4tracker.track({ 5 serviceName: '<MW_SERVICE_NAME>', 6 accessToken: '<MW_API_KEY>', 7 target: 'https://<MW_UID>.middleware.io:443', 8 projectName: '<MW_PROJECT_NAME>', 9});
1node --import ./instrumentation.mjs app.mjs
- TypeScript Create instrumentation.ts:Dev / quick start (ts-node):
1// instrumentation.ts 2import * as tracker from '@middleware.io/node-apm'; 3 4tracker.track({ 5 serviceName: '<MW_SERVICE_NAME>', 6 accessToken: '<MW_API_KEY>', 7 target: 'https://<MW_UID>.middleware.io:443', 8 projectName: '<MW_PROJECT_NAME>', 9});
ESM TypeScript (optional):1npx ts-node --require ./instrumentation.ts app.ts
Production best practice: compile to JS and run with the JS instructions.1node --loader ts-node/esm --import ./instrumentation.ts app.ts
1npx tsc 2node --require ./instrumentation.js dist/app.js
Alternate initialisation: you may place the tracker code at the very top of your app’s entry file (before any other import
/require
). Using a preload file is recommended as it avoids accidental reordering.
3 Cloud Run environment variables
Add the following env vars to your Cloud Run service (Console → Cloud Run → Service → Edit & deploy → Variables & Secrets):
1OTEL_NODE_RESOURCE_DETECTORS=all
Reduce missing spans in serverless:
1OTEL_BSP_SCHEDULE_DELAY=500
Lower values flush more frequently and can reduce data loss at shutdown (modestly higher network cost).
You can also gracefully shut down programmatically (next section) to ensure final export on SIGTERM
.
4 Graceful Shutdown (recommended)
Add a shutdown handler so Cloud Run’s SIGTERM
can trigger a final export before the 10‑second termination window.
1// main app (Node.js)
2const tracker = require('@middleware.io/node-apm');
3
4// If not already initialised via preload:
5// tracker.track({ serviceName: '<MW_SERVICE_NAME>', accessToken: '<MW_API_KEY>', target: '<MW_TARGET_URL>' });
6
7process.on('SIGTERM', async () => {
8 console.log('SIGTERM received -- flushing Middleware telemetry...');
9 try {
10 await tracker.sdkShutdown();
11 } finally {
12 process.exit(0);
13 }
14});
On some platforms you may also wish to listen for SIGINT (manual stop) in local/dev.
5 Custom Logs & Error Stacks (optional)
1// anywhere in your code
2const tracker = require('@middleware.io/node-apm');
3
4tracker.info('Info sample');
5tracker.warn('Warning sample');
6tracker.debug('Debugging sample');
7tracker.error('Error sample');
8
9// Record an Error object and stack
10try {
11 throw new Error('oh error!');
12} catch (e) {
13 tracker.errorRecord(e);
14}
1 Install the package
1dotnet add package MW.APM
For Visual Studio, go to Tools → NuGet Package Manager → Manage NuGet Packages for Solution → Browse → MW.APM → Install
2 Wire up Instrumentation & Logging
In Program.cs:
1var builder = WebApplication.CreateBuilder(args);
2
3var configuration = new ConfigurationBuilder()
4 .SetBasePath(Directory.GetCurrentDirectory())
5 .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
6 .AddEnvironmentVariables()
7 .Build();
8
9builder.Services.ConfigureMWInstrumentation(configuration);
10
11builder.Logging.AddConfiguration(configuration.GetSection("Logging"));
12builder.Logging.AddConsole();
13builder.Logging.SetMinimumLevel(LogLevel.Debug);
14
15var app = builder.Build();
16
17// After app creation (so MW can capture logs)
18Logger.Init(app.Services.GetRequiredService<ILoggerFactory>());
19
20app.MapGet("/health", () => Results.Ok("ok"));
21
22app.Run();
3 Configure Your Middleware Account Settings
appsettings.json:
1{
2 "MW": {
3 "ApiKey": "<MW_API_KEY>",
4 "TargetURL": "https://<MW_UID>.middleware.io:443",
5 "ServiceName": "<MW_SERVICE_NAME>",
6 "ProjectName": "<MW_PROJECT_NAME>",
7
8 "ConsoleExporter": "true",
9 "ExcludeLinks": "[\"/health\"]",
10
11 // Collectors (toggle as needed)
12 "ApmCollectMetrics": "true",
13 "ApmCollectTraces": "true",
14 "ApmCollectLogs": "true"
15 },
16 "Logging": {
17 "LogLevel": {
18 "Default": "Information",
19 "Microsoft.AspNetCore": "Warning"
20 }
21 }
22}
This is a one‑time configuration. Each container that starts on Cloud Run will initialise the .NET instrumentation.
1 Add middleware-io to requirements.txt
1middleware-io>=2.4.1
2setuptools
This ensures the package is installed when your Cloud Run container builds.
2 Setup Options
Auto instrumentation allows you to monitor your Python application without modifying the application code. This can be achieved by setting environment variables that configure the application with Middleware Application Performance Monitoring (APM) tool.
Setup Virtual Environment
1python -m venv myenv
2source myenv/bin/activate
Install Requirements To install requirements mentioned in the file requirements.txt run the command in your terminal:
1pip install -r requirements.txt
Install OpenTelemetry automatic instrumentation libraries: Run the command to install instrumentation library:
1middleware-bootstrap -a install
The middleware-bootstrap -a install
command reads through the list of packages installed in your active site-packages
folder, and installs the corresponding instrumentation libraries for these packages, if applicable. For example, if you already installed the flask
package, running middleware-bootstrap -a install
will install opentelemetry-instrumentation-flask
for you. The Middleware Python agent will use monkey patching to modify functions in these libraries at runtime.
Ensure library is installed properly with command:
1pip list | grep -i flask
2
3Flask
4opentelemetry-instrumentation-flask
For Containerized Application add command in dockerfile:
1RUN middleware-bootstrap -a install
Example:
You can set the following environment variables in your shell to auto instrument:
1export MW_API_KEY='<MW_API_KEY>'
2export MW_TARGET='https://<MW_UID>.middleware.io:443'
3export MW_SERVICE_NAME='MyFlaskServer'
4middleware-run python app.py
If you prefer more control and want to integrate APM directly into your application, you can use a middleware function like mw_tracker
. This method requires adding a specific function call in your application's code. Set these environment variables in your Cloud Run service configuration or Dockerfile:
1MW_TRACKER=True
Then, initialize in your code with:
Example: Using mw_tracker Function from Middleware
1from flask import Flask
2# Import the mw_tracker from middleware to your app
3from middleware import mw_tracker, MWOptions, record_exception, DETECT_GCP
4
5 mw_tracker(
6 MWOptions(
7 access_token="<MW_API_KEY>",
8 target="https://<MW_UID>.middleware.io:443",
9 console_exporter=True,
10 debug_log_file=True,
11 service_name="MyPythonServer",
12 otel_propagators = "b3,tracecontext",
13 custom_resource_attributes="call_id=12345678, request_id=987654321",
14 detectors=[DETECT_GCP]
15 )
16 )
17
18app = Flask(__name__)
19
20@app.route('/')
21def index():
22 return "Hello, World!"
23
24if __name__ == "__main__":
25 app.run()
For more advanced configuration of MWOptions check out this list of all possible options here.
3 Add Custom Instrumentation (Optional)
If you need to add custom spans or attributes, see Python APM configuration.
Troubleshooting If you encounter any issues, visit Python troubleshooting.
Troubleshooting:
- Nothing appears in Traces
- Ensure the tracker initialises before any other imports.
- Check that the container actually runs with
--require
/--import
(printprocess.execArgv
). - Confirm
Node.js 18+
in the container (node --version
).
- ESM errors with
--require
→ use the ESM path (--import
) or move initialisation to the entry file’s first line. - TypeScript only works locally → compile to JS in CI/CD and run the JS entry in Cloud Run.
- Spans missing on shutdown → set
OTEL_BSP_SCHEDULE_DELAY=500
and add theSIGTERM
handler that callstracker.sdkShutdown()
. - Networking: ensure outbound egress to
<MW_TARGET_URL>
is allowed by your VPC/Egress settings.
Need assistance or want to learn more about Middleware? Contact our support team at [email protected] or join our Slack channel.