Guide · May 2026

How to Log OpenAI API Calls in Production

OpenAI's dashboard shows you aggregate token usage and costs. What it doesn't show you is which specific calls failed, what prompts triggered unexpected responses, how latency varies by user, or which features are actually consuming your budget. For that you need your own logging. This guide covers everything you need to know.

OpenAIGPT-4oProduction loggingAPI monitoringToken tracking
Table of contents
Why OpenAI's built-in logging isn't enoughWhat to log on every API callBasic logging with fetchLogging with the OpenAI SDKBuilding a reusable wrapperUsing the Logwick SDK (fastest approach)Tracking costs per user and featureLogging errors and timeoutsLogging LangChain + OpenAI callsViewing and searching your logs

Why OpenAI's built-in logging isn't enough

OpenAI provides a usage dashboard that shows total tokens consumed and estimated costs. It's useful for billing but useless for debugging. When a user reports that your AI feature gave them a bad response, the OpenAI dashboard can't tell you what prompt triggered it, what the model returned, how long it took, or whether the problem is reproducible.

What you actually need in production:

Input/output logging
The exact prompt sent and response received for every call, so you can reproduce and debug issues.
Per-request token counts
Not just total usage, but tokens per call, per user, per feature — so you know what's expensive.
Latency tracking
How long each call takes. GPT-4o can vary from 500ms to 30 seconds depending on prompt length and load.
Error logging
Rate limit errors, timeouts, and content policy rejections need to be tracked separately from successful calls.
User attribution
Which of your users or customers triggered each call, so you can debug customer-specific issues and allocate costs.

What to log on every OpenAI API call

At minimum, every log entry should capture these fields:

Log fields
{
  agent:      "gpt-4o",          // model used
  action:     "email_draft",     // what the call was for
  status:     "success",         // success | error | pending
  input:      userPrompt,        // the prompt sent
  output:     responseText,      // the response received
  tokens:     312,               // total tokens used
  latency_ms: 1842,              // how long it took
  cost_usd:   0.0021,            // estimated cost
  user:       "user@example.com" // who triggered it
}

The action field is important — it's the business context for the call. Not just "gpt-4o was called" but "an email was drafted". This lets you filter logs by feature and understand which parts of your product are working.

Basic logging with fetch

The simplest approach — make your OpenAI call, then immediately fire a log. The log call is fire-and-forget so it never blocks your response.

JavaScript
const start = Date.now()

// Your existing OpenAI call — unchanged
const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [{ role: 'user', content: userPrompt }]
})

const output = response.choices[0].message.content
const latency = Date.now() - start

// Log immediately after — fire and forget
fetch('https://logwick.io/api/v1/logs', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + process.env.LOGWICK_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    agent:      'gpt-4o',
    action:     'email_draft',   // what this call was for
    status:     'success',
    input:      userPrompt,
    output:     output,
    tokens:     response.usage.total_tokens,
    latency_ms: latency,
    user:       currentUser.email,
    cost_usd:   response.usage.total_tokens * 0.000005
  })
}).catch(() => {}) // never throws, never blocks

return output

Logging with the OpenAI SDK

The same pattern works with the official OpenAI Node.js SDK. The SDK returns the same response object including usage.total_tokens and usage.prompt_tokens.

JavaScript — OpenAI SDK
import OpenAI from 'openai'
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })

async function callWithLogging(prompt, action, user) {
  const start = Date.now()
  let status = 'success'
  let output = ''
  let tokens = 0

  try {
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [{ role: 'user', content: prompt }]
    })
    output = response.choices[0].message.content
    tokens = response.usage.total_tokens
  } catch (err) {
    status = 'error'
    output = err.message
  }

  // Log regardless of success or failure
  fetch('https://logwick.io/api/v1/logs', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + process.env.LOGWICK_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      agent: 'gpt-4o',
      action,
      status,
      input: prompt,
      output,
      tokens,
      latency_ms: Date.now() - start,
      user
    })
  }).catch(() => {})

  if (status === 'error') throw new Error(output)
  return output
}

Building a reusable wrapper

If you're making OpenAI calls in multiple places, a wrapper function avoids repeating the logging code everywhere. Here's a clean pattern that works across your entire codebase.

lib/ai.js — reusable wrapper
// lib/ai.js
import OpenAI from 'openai'

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
const LOGWICK_KEY = process.env.LOGWICK_API_KEY

export async function chat(messages, { action, user, model = 'gpt-4o' } = {}) {
  const start = Date.now()
  const prompt = messages.map(m => m.content).join('\n')

  try {
    const response = await openai.chat.completions.create({ model, messages })
    const output = response.choices[0].message.content

    log({ action, user, model, status: 'success',
      input: prompt, output,
      tokens: response.usage.total_tokens,
      latency_ms: Date.now() - start })

    return output
  } catch (err) {
    log({ action, user, model, status: 'error',
      input: prompt, output: err.message,
      latency_ms: Date.now() - start })
    throw err
  }
}

function log(data) {
  if (!LOGWICK_KEY) return
  fetch('https://logwick.io/api/v1/logs', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + LOGWICK_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ agent: data.model, ...data })
  }).catch(() => {})
}

// Usage anywhere in your app:
// import { chat } from './lib/ai'
// const reply = await chat(messages, { action: 'email_draft', user: req.user.email })

Using the Logwick SDK (fastest approach)

The Logwick SDK includes a built-in OpenAI wrapper that handles all the timing, error handling, and token counting automatically.

Install
npm install logwick
JavaScript
import { LogwickClient } from 'logwick'

const logwick = new LogwickClient({ apiKey: process.env.LOGWICK_API_KEY })

// Wrap your existing OpenAI call — nothing else changes
const result = await logwick.openai(
  () => openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: prompt }]
  }),
  { action: 'email_draft', user: req.user.email }
)

// result is the normal OpenAI response object
const reply = result.choices[0].message.content

The wrapper automatically captures timing, token usage, the input prompt, the output, and logs errors. You get a full audit trail for every call with one line of change.

Tracking costs per user and feature

OpenAI's dashboard shows total spend. It doesn't tell you which feature or customer is driving it. By logging the user and action fields alongside cost_usd, you can answer questions like: which customer costs the most to serve, and which feature is most expensive.

GPT-4o cost calculation
// GPT-4o pricing (as of 2026):
// Input:  $2.50 per million tokens
// Output: $10.00 per million tokens

function calculateCost(usage) {
  const inputCost  = (usage.prompt_tokens / 1_000_000) * 2.50
  const outputCost = (usage.completion_tokens / 1_000_000) * 10.00
  return inputCost + outputCost
}

// Then log it:
logwick.fire({
  agent:    'gpt-4o',
  action:   'email_draft',
  tokens:   response.usage.total_tokens,
  cost_usd: calculateCost(response.usage),
  user:     currentUser.email
})

Logging errors and timeouts

OpenAI errors fall into three categories: rate limit errors (429), timeout errors, and content policy rejections. Each needs different handling. Log all of them with status: 'error' so you can track error rates over time.

JavaScript — error handling
try {
  const response = await openai.chat.completions.create({ ... })
  logwick.fire({ status: 'success', ...data })
} catch (err) {
  // Log the error with full context
  logwick.fire({
    agent:    'gpt-4o',
    action:   action,
    status:   'error',
    input:    prompt,
    output:   err.message,
    latency_ms: Date.now() - start,
    user:     user,
    metadata: {
      error_type: err.constructor.name,
      error_code: err.status,
      // Rate limit: 429, Timeout: ETIMEDOUT, Policy: 400
    }
  })
  throw err
}

Logging LangChain + OpenAI calls

If you're using LangChain, the cleanest approach is a callback handler that automatically logs every LLM call in your chain without modifying each call individually.

JavaScript — LangChain callback
import { LogwickCallbackHandler } from 'logwick'
import { LLMChain } from 'langchain/chains'

const handler = new LogwickCallbackHandler(logwick, {
  user: req.user.email
})

// Every LLM call in this chain is logged automatically
const chain = new LLMChain({
  llm,
  prompt,
  callbacks: [handler]
})

const result = await chain.call({ input: userQuery })
// Logwick has already logged the call — no extra code needed

Viewing and searching your logs

Once you're logging, the Logwick dashboard gives you a searchable, filterable view of all your OpenAI calls. You can filter by status to find all errors, search by user to debug a customer issue, or filter by action to understand the performance of a specific feature.

You can also query logs via API or stream them in real time:

Query your logs via API
# Get all errors from the last 24 hours
curl "https://logwick.io/api/v1/logs?status=error&from=2026-05-01" \
  -H "Authorization: Bearer sk-lw-your-key"

# Stream logs in real time
curl -N "https://logwick.io/api/v1/logs/stream?status=error" \
  -H "Authorization: Bearer sk-lw-your-key"

# Get stats
curl "https://logwick.io/api/v1/stats?days=7" \
  -H "Authorization: Bearer sk-lw-your-key"

If you use Claude Desktop, you can also connect the Logwick MCP server and ask questions in plain English: "Show me all failed email_draft calls from yesterday" or "How much did we spend on GPT-4o this week?"

Start logging your OpenAI calls today

Free tier includes 5,000 logs/month. No credit card required. Up and running in 3 minutes.

Get started free →Read the docs