CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pulumi--pulumi

Pulumi's Node.js SDK for infrastructure-as-code platform that allows you to create, deploy, and manage infrastructure using familiar programming languages and tools.

85

1.02x
Overview
Eval results
Files

logging-diagnostics.mddocs/

Logging and Diagnostics

Pulumi's logging system provides structured logging and error handling for infrastructure deployment diagnostics, enabling better observability and debugging of infrastructure operations.

Logging Functions

function hasErrors(): boolean;
function debug(msg: string, resource?: Resource, streamId?: number, ephemeral?: boolean): void;
function info(msg: string, resource?: Resource, streamId?: number, ephemeral?: boolean): void;
function warn(msg: string, resource?: Resource, streamId?: number, ephemeral?: boolean): void;
function error(msg: string, resource?: Resource, streamId?: number, ephemeral?: boolean): void;

Error Classes

class RunError extends Error {
  readonly __pulumiRunError: boolean;
  static isInstance(obj: any): obj is RunError;
}

class ResourceError extends Error {
  readonly __pulumResourceError: boolean;
  resource: Resource | undefined;
  hideStack?: boolean;
  
  constructor(message: string, resource: Resource | undefined, hideStack?: boolean);
  static isInstance(obj: any): obj is ResourceError;
}

class InputPropertyError extends Error {
  readonly __pulumiInputPropertyError: boolean;
  propertyPath: string;
  reason: string;
  
  constructor(args: InputPropertyErrorDetails);
  static isInstance(obj: any): obj is InputPropertyError;
}

class InputPropertiesError extends Error {
  readonly __pulumiInputPropertiesError: boolean;
  reasons: InputPropertyError[];
  
  constructor(errors: InputPropertyError[]);
  static isInstance(obj: any): obj is InputPropertiesError;
}

interface InputPropertyErrorDetails {
  propertyPath: string;
  reason: string;
}

function isGrpcError(err: Error): boolean;

Usage Examples

Basic Logging

import * as pulumi from "@pulumi/pulumi";

// Simple logging without resource context
pulumi.log.info("Starting infrastructure deployment");
pulumi.log.debug("Configuration loaded successfully");
pulumi.log.warn("Using default values for missing configuration");

// Check for errors
if (pulumi.log.hasErrors()) {
  pulumi.log.error("Deployment failed due to previous errors");
  process.exit(1);
}

Resource-Contextual Logging

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const bucket = new aws.s3.Bucket("my-bucket", {
  acl: "private",
});

// Log with resource context
pulumi.log.info("Creating S3 bucket with private ACL", bucket);
pulumi.log.debug(`Bucket configuration: ${JSON.stringify({
  acl: "private",
  versioning: false,
})}`, bucket);

bucket.id.apply(id => {
  pulumi.log.info(`S3 bucket created with ID: ${id}`, bucket);
});

// Warning with resource context
if (!bucket.versioning) {
  pulumi.log.warn("Bucket versioning is disabled - consider enabling for production", bucket);
}

Error Handling

import * as pulumi from "@pulumi/pulumi";

// Throw RunError for clean program termination
if (!process.env.AWS_REGION) {
  throw new pulumi.RunError("AWS_REGION environment variable is required");
}

// Throw ResourceError for resource-specific issues
const database = new aws.rds.Instance("prod-db", {
  engine: "postgres",
  instanceClass: "db.t3.micro",
});

// Validate resource configuration
database.engine.apply(engine => {
  if (engine !== "postgres" && engine !== "mysql") {
    throw new pulumi.ResourceError(
      `Unsupported database engine: ${engine}`,
      database,
      false // Don't hide stack trace
    );
  }
});

Input Validation Errors

import * as pulumi from "@pulumi/pulumi";

function validateBucketName(name: string): void {
  const errors: pulumi.InputPropertyError[] = [];
  
  if (!name) {
    errors.push(new pulumi.InputPropertyError({
      propertyPath: "bucketName",
      reason: "bucketName is required"
    }));
  }
  
  if (name && name.length < 3) {
    errors.push(new pulumi.InputPropertyError({
      propertyPath: "bucketName",
      reason: "bucketName must be at least 3 characters long"
    }));
  }
  
  if (name && !/^[a-z0-9.-]+$/.test(name)) {
    errors.push(new pulumi.InputPropertyError({
      propertyPath: "bucketName",
      reason: "bucketName can only contain lowercase letters, numbers, periods, and hyphens"
    }));
  }
  
  if (errors.length > 0) {
    throw new pulumi.InputPropertiesError(errors);
  }
}

// Use validation
try {
  validateBucketName("MyBucket"); // Will throw error
} catch (error) {
  if (pulumi.InputPropertiesError.isInstance(error)) {
    error.reasons.forEach(reason => {
      pulumi.log.error(`Validation failed for ${reason.propertyPath}: ${reason.reason}`);
    });
  }
}

Structured Logging Patterns

import * as pulumi from "@pulumi/pulumi";

class InfrastructureLogger {
  private context: string;
  
  constructor(context: string) {
    this.context = context;
  }
  
  logOperation(operation: string, resource?: pulumi.Resource): void {
    pulumi.log.info(`[${this.context}] ${operation}`, resource);
  }
  
  logConfig(config: any, resource?: pulumi.Resource): void {
    pulumi.log.debug(`[${this.context}] Configuration: ${JSON.stringify(config)}`, resource);
  }
  
  logWarning(message: string, resource?: pulumi.Resource): void {
    pulumi.log.warn(`[${this.context}] ${message}`, resource);
  }
  
  logError(message: string, error?: Error, resource?: pulumi.Resource): void {
    const errorMsg = error ? `${message}: ${error.message}` : message;
    pulumi.log.error(`[${this.context}] ${errorMsg}`, resource);
  }
}

// Usage
const logger = new InfrastructureLogger("WebApp");

const bucket = new aws.s3.Bucket("web-assets", {
  acl: "public-read",
});

logger.logOperation("Creating S3 bucket for web assets", bucket);
logger.logConfig({ acl: "public-read", versioning: false }, bucket);

bucket.id.apply(id => {
  logger.logOperation(`S3 bucket created: ${id}`, bucket);
});

Conditional Logging

import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();
const debugMode = config.getBoolean("debug") || false;
const environment = config.get("environment") || "development";

function conditionalLog(level: "debug" | "info" | "warn" | "error", message: string, resource?: pulumi.Resource): void {
  // Only log debug messages in debug mode
  if (level === "debug" && !debugMode) {
    return;
  }
  
  // Add environment prefix in production
  const prefixedMessage = environment === "production" ? `[PROD] ${message}` : message;
  
  switch (level) {
    case "debug":
      pulumi.log.debug(prefixedMessage, resource);
      break;
    case "info":
      pulumi.log.info(prefixedMessage, resource);
      break;
    case "warn":
      pulumi.log.warn(prefixedMessage, resource);
      break;
    case "error":
      pulumi.log.error(prefixedMessage, resource);
      break;
  }
}

// Usage
conditionalLog("debug", "Detailed configuration loaded");
conditionalLog("info", "Starting deployment");
conditionalLog("warn", "Using development configuration in production");

Error Recovery Patterns

import * as pulumi from "@pulumi/pulumi";

async function createResourceWithRetry<T extends pulumi.Resource>(
  createFn: () => T,
  maxRetries: number = 3
): Promise<T> {
  let lastError: Error | undefined;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      pulumi.log.debug(`Attempt ${attempt}/${maxRetries} to create resource`);
      return createFn();
    } catch (error) {
      lastError = error as Error;
      pulumi.log.warn(`Attempt ${attempt} failed: ${error.message}`);
      
      if (attempt === maxRetries) {
        pulumi.log.error(`All ${maxRetries} attempts failed`);
        break;
      }
      
      // Wait before retry
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
  
  throw new pulumi.RunError(`Failed to create resource after ${maxRetries} attempts: ${lastError?.message}`);
}

// Usage
const bucket = await createResourceWithRetry(() => new aws.s3.Bucket("retry-bucket", {
  acl: "private",
}));

Diagnostic Information

import * as pulumi from "@pulumi/pulumi";

function logDiagnosticInfo(): void {
  pulumi.log.info(`Project: ${pulumi.getProject()}`);
  pulumi.log.info(`Stack: ${pulumi.getStack()}`);
  pulumi.log.info(`Organization: ${pulumi.getOrganization()}`);
  
  const config = new pulumi.Config();
  const allConfig = Object.keys(process.env)
    .filter(key => key.startsWith('PULUMI_CONFIG_'))
    .reduce((acc, key) => {
      const configKey = key.replace('PULUMI_CONFIG_', '').toLowerCase();
      acc[configKey] = config.get(configKey) || '<not set>';
      return acc;
    }, {} as Record<string, string>);
  
  pulumi.log.debug(`Configuration: ${JSON.stringify(allConfig)}`);
}

// Log diagnostic info at startup
logDiagnosticInfo();

Performance Logging

import * as pulumi from "@pulumi/pulumi";

class PerformanceLogger {
  private timers: Map<string, number> = new Map();
  
  startTimer(label: string): void {
    this.timers.set(label, Date.now());
    pulumi.log.debug(`Started timer: ${label}`);
  }
  
  endTimer(label: string, resource?: pulumi.Resource): void {
    const startTime = this.timers.get(label);
    if (startTime) {
      const duration = Date.now() - startTime;
      pulumi.log.info(`${label} completed in ${duration}ms`, resource);
      this.timers.delete(label);
    }
  }
}

const perfLogger = new PerformanceLogger();

perfLogger.startTimer("S3 Bucket Creation");
const bucket = new aws.s3.Bucket("perf-bucket");
bucket.id.apply(() => {
  perfLogger.endTimer("S3 Bucket Creation", bucket);
});

Best Practices

  • Use appropriate log levels (debug for detailed info, info for normal operations, warn for potential issues, error for failures)
  • Include resource context in logs when possible for better debugging
  • Use structured logging patterns for consistency
  • Implement conditional logging based on environment or debug flags
  • Handle errors gracefully with proper error types
  • Log performance metrics for long-running operations
  • Include diagnostic information in deployment logs
  • Use InputPropertyError and InputPropertiesError for validation failures
  • Avoid logging sensitive information (passwords, API keys)
  • Implement retry logic with appropriate logging
  • Use ephemeral logging for temporary debug information
  • Log configuration validation results
  • Include stack traces for debugging when appropriate

Install with Tessl CLI

npx tessl i tessl/npm-pulumi--pulumi

docs

asset-management.md

automation.md

configuration.md

dynamic-resources.md

index.md

logging-diagnostics.md

output-system.md

provider-development.md

resource-management.md

runtime-operations.md

stack-references.md

utilities.md

tile.json