@azure/mcp-win32-x64
Version:
Azure MCP Server - Model Context Protocol implementation for Azure, for win32 on x64
302 lines (228 loc) • 8.47 kB
Markdown
# Basic Azure Monitor Setup for Node.js with Bunyan Logging
This guide shows how to add Azure Monitor OpenTelemetry to a Node.js application using Bunyan for logging.
## Prerequisites
- Node.js 14.x or higher
- npm or yarn
- Node.js application with Bunyan (`bunyan` package)
- Azure Application Insights resource
## Step 1: Install Package
```bash
npm install @azure/monitor-opentelemetry
```
## Step 2: Initialize at Startup
Create or update your main entry point (typically `index.js` or `server.js`):
```javascript
// IMPORTANT: This must be the first line, before any other imports
const { useAzureMonitor } = require('@azure/monitor-opentelemetry');
// Initialize Azure Monitor with Bunyan log collection enabled
useAzureMonitor({
azureMonitorExporterOptions: {
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING
},
instrumentationOptions: {
bunyan: { enabled: true }
}
});
// Now load your application code
const express = require('express');
const bunyan = require('bunyan');
// Create Bunyan logger
const logger = bunyan.createLogger({
name: 'my-app',
level: process.env.LOG_LEVEL || 'info',
serializers: bunyan.stdSerializers
});
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
// Request logging middleware
app.use((req, res, next) => {
logger.info({ req }, 'Incoming request');
next();
});
app.get('/api/users', (req, res) => {
logger.info('Fetching users');
res.json([{ id: 1, name: 'Alice' }]);
});
app.listen(port, () => {
logger.info({ port }, 'Server listening');
});
```
## Step 3: Configure Connection String
Create a `.env` file in your project root:
```env
APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://...
LOG_LEVEL=info
PORT=3000
```
Install `dotenv` to load environment variables:
```bash
npm install dotenv
```
Load it at the very top of your entry file:
```javascript
require('dotenv').config();
const { useAzureMonitor } = require('@azure/monitor-opentelemetry');
// ... rest of code
```
## What Gets Collected
With Bunyan instrumentation enabled, the following log data is sent to Azure Monitor:
- **Log level**: fatal, error, warn, info, debug, trace
- **Log message**: The log content
- **Log fields**: All additional fields passed to the logger
- **Timestamp**: When the log was created
- **Trace context**: Correlation with distributed traces
## Log Level Mapping
Bunyan log levels are mapped to Application Insights severity levels:
| Bunyan Level | Numeric | Application Insights Severity |
|-------------|---------|------------------------------|
| fatal | 60 | Critical |
| error | 50 | Error |
| warn | 40 | Warning |
| info | 30 | Information |
| debug | 20 | Verbose |
| trace | 10 | Verbose |
## Step 4: Structured Logging Best Practices
Bunyan excels at structured logging. Use its features effectively:
### Add Context to Logs
```javascript
// Good: Structured logging with context
logger.info({ userId: user.id, email: user.email }, 'User created');
// Good: Error logging with error serializer
logger.error({ err: error, email: req.body.email }, 'Failed to create user');
// Good: Request/response logging
logger.info({ req, res }, 'Request completed');
```
### Child Loggers for Request Context
```javascript
app.use((req, res, next) => {
// Create a child logger with request context
req.log = logger.child({
requestId: req.headers['x-request-id'] || Math.random().toString(36).substr(2, 9),
path: req.path,
method: req.method
});
next();
});
app.get('/api/users/:id', (req, res) => {
// Use the request-scoped logger
req.log.info({ userId: req.params.id }, 'Fetching user');
try {
const user = getUserById(req.params.id);
req.log.debug({ user }, 'User found');
res.json(user);
} catch (error) {
req.log.error({ err: error, userId: req.params.id }, 'User not found');
res.status(404).json({ error: 'User not found' });
}
});
```
### Custom Serializers
```javascript
const logger = bunyan.createLogger({
name: 'my-app',
serializers: {
...bunyan.stdSerializers,
user: (user) => ({
id: user.id,
email: user.email,
role: user.role
}),
order: (order) => ({
id: order.id,
total: order.total,
itemCount: order.items.length
})
}
});
// Use custom serializers
logger.info({ user }, 'User logged in');
logger.info({ order }, 'Order created');
```
## Step 5: Custom Telemetry (Optional)
Combine logging with custom spans:
```javascript
const { trace } = require('@opentelemetry/api');
app.post('/api/orders', async (req, res) => {
const tracer = trace.getTracer('my-app');
await tracer.startActiveSpan('create-order', async (span) => {
req.log.info({ itemCount: req.body.items.length }, 'Creating order');
try {
const order = await createOrder(req.body);
req.log.info({ order }, 'Order created successfully');
span.setAttribute('order.id', order.id);
res.status(201).json(order);
} catch (error) {
req.log.error({ err: error }, 'Failed to create order');
span.recordException(error);
span.setStatus({ code: 2, message: error.message });
res.status(500).json({ error: 'Failed to create order' });
} finally {
span.end();
}
});
});
```
## Viewing Logs in Azure Portal
1. Open your Application Insights resource in Azure Portal
2. Navigate to "Logs" under Monitoring
3. Query traces table:
```kusto
traces
| where timestamp > ago(1h)
| where customDimensions.name == "my-app"
| project timestamp, message, severityLevel, customDimensions
| order by timestamp desc
```
4. Use "Transaction search" to see logs correlated with requests
## Troubleshooting
### Bunyan logs not appearing
1. Ensure `bunyan` instrumentation is enabled in options
2. Verify `useAzureMonitor()` is called **before** importing `bunyan`
3. Check that the connection string is valid
### Using Bunyan with Next.js
> **Note**: The standard setup in this guide (Steps 1-5) works for Express, Fastify, NestJS, and other standard Node.js frameworks with no special configuration. The issues below are **specific to Next.js** because it bundles server-side code with webpack.
Bunyan has optional native dependencies (`dtrace-provider`, `source-map-support`) that Next.js's webpack bundler cannot resolve. You'll see errors like:
- `Module not found: Can't resolve 'source-map-support'`
- `Module not found: Can't resolve './src/build'` (from `dtrace-provider`)
**Fix**: Add `bunyan` to both `serverComponentsExternalPackages` and `webpack.externals` in your `next.config.js`:
```javascript
const nextConfig = {
experimental: {
instrumentationHook: true,
serverComponentsExternalPackages: [
'@azure/monitor-opentelemetry',
// ... other OpenTelemetry packages ...
'bunyan',
],
},
webpack: (config, { isServer }) => {
if (isServer) {
config.externals = config.externals || [];
config.externals.push({
// ... other OpenTelemetry externals ...
bunyan: 'commonjs bunyan',
});
}
return config;
},
};
```
In Next.js, also mark API routes using bunyan with `export const runtime = 'nodejs'` to ensure they run on the Node.js runtime (not Edge). This is a Next.js-specific concept and does not apply to standard Node.js apps.
See the Next.js Setup Guide(see in basic-setup-nextjs.md) for the full Next.js + bunyan configuration.
### Log fields not appearing correctly
Ensure you're using the correct Bunyan logging format:
```javascript
// Correct: fields object first, then message
logger.info({ userId: 123 }, 'User action');
// Incorrect: message first (fields won't be captured properly)
logger.info('User action', { userId: 123 });
```
### Too many logs being sent
Control log volume by adjusting the log level:
```javascript
const logger = bunyan.createLogger({
name: 'my-app',
level: process.env.NODE_ENV === 'production' ? 'warn' : 'debug'
});
```