Bird
Raised Fist0
Denoframework~15 mins

Error handling and logging in Deno - Deep Dive

Choose your learning style10 modes available
Overview - Error handling and logging
What is it?
Error handling and logging in Deno means catching problems when they happen and writing down useful information about them. This helps developers understand what went wrong and fix issues faster. Logging records events or errors in a program, while error handling manages how the program reacts to those errors. Together, they keep programs reliable and easier to maintain.
Why it matters
Without error handling and logging, programs can crash unexpectedly or behave strangely without any clues why. This makes fixing problems slow and frustrating, especially in real-world apps where users expect smooth experiences. Good error handling prevents crashes and keeps apps running, while logging provides a history of what happened, helping developers find and solve bugs quickly.
Where it fits
Before learning error handling and logging, you should understand basic Deno programming, including async/await and promises. After mastering this topic, you can explore testing, debugging tools, and advanced monitoring solutions to keep your apps healthy in production.
Mental Model
Core Idea
Error handling catches problems so the program can respond safely, while logging records what happened to help understand and fix issues.
Think of it like...
It's like a car dashboard: error handling is the safety system that stops the car from crashing badly, and logging is the black box that records what happened during the drive.
┌───────────────┐      ┌───────────────┐
│   Program     │─────▶│   Error       │
│   Runs       │      │   Handling    │
└───────────────┘      └───────────────┘
         │                      │
         ▼                      ▼
  ┌───────────────┐      ┌───────────────┐
  │   Logging     │◀─────│   Errors &    │
  │   Records    │      │   Events      │
  └───────────────┘      └───────────────┘
Build-Up - 7 Steps
1
FoundationBasics of Try-Catch in Deno
🤔
Concept: Learn how to use try-catch blocks to catch errors in synchronous and asynchronous code.
In Deno, you use try-catch to handle errors. Put code that might fail inside try { }, and handle errors in catch (error) { }. Example: try { const data = JSON.parse('{ invalid json }'); } catch (error) { console.log('Caught error:', error.message); } This prevents the program from crashing and lets you respond to errors.
Result
The program catches the JSON parsing error and prints 'Caught error: Unexpected token i in JSON at position 2' instead of crashing.
Understanding try-catch is the foundation of error handling, letting your program stay in control even when things go wrong.
2
FoundationUsing Deno's Built-in Logging Module
🤔
Concept: Learn how to use Deno's standard logging library to record messages and errors.
Deno provides a logging module to write logs with different levels like INFO, WARNING, and ERROR. Example: import * as log from 'https://deno.land/std@0.203.0/log/mod.ts'; await log.setup({ handlers: { console: new log.handlers.ConsoleHandler('DEBUG'), }, loggers: { default: { level: 'DEBUG', handlers: ['console'], }, }, }); log.info('App started'); log.error('Something went wrong');
Result
Logs appear in the console with timestamps and levels, helping track app behavior and errors.
Using a structured logging library helps keep logs consistent and easy to filter or search.
3
IntermediateHandling Async Errors with Try-Catch
🤔Before reading on: do you think try-catch works directly with async functions or do you need special handling? Commit to your answer.
Concept: Learn how to catch errors from asynchronous code using try-catch with async/await.
Async functions return promises that may reject with errors. Using try-catch around await lets you catch these errors. Example: async function fetchData() { try { const response = await fetch('https://invalid.url'); const data = await response.json(); } catch (error) { console.error('Fetch failed:', error.message); } } fetchData();
Result
If the fetch fails, the error is caught and logged instead of crashing the program.
Knowing how try-catch works with async/await prevents unhandled promise rejections and keeps your app stable.
4
IntermediateCustom Error Classes for Clearer Handling
🤔Before reading on: do you think all errors should be handled the same way, or can custom errors help? Commit to your answer.
Concept: Create custom error types to distinguish different error causes and handle them specifically.
You can extend the built-in Error class to make your own error types. Example: class ValidationError extends Error { constructor(message) { super(message); this.name = 'ValidationError'; } } function validate(input) { if (!input) throw new ValidationError('Input is required'); } try { validate(''); } catch (error) { if (error instanceof ValidationError) { console.log('Validation failed:', error.message); } else { throw error; } }
Result
The program catches validation errors separately, allowing tailored responses.
Custom errors let you write clearer, more maintainable error handling by knowing exactly what went wrong.
5
IntermediateConfiguring Log Levels and Outputs
🤔
Concept: Learn how to set different log levels and send logs to files or other outputs.
Deno's logging module lets you control what logs appear by level and where they go. Example: import * as log from 'https://deno.land/std@0.203.0/log/mod.ts'; await log.setup({ handlers: { file: new log.handlers.FileHandler('WARNING', { filename: './app.log' }), console: new log.handlers.ConsoleHandler('DEBUG'), }, loggers: { default: { level: 'DEBUG', handlers: ['console', 'file'], }, }, }); log.debug('Debug info'); log.warning('Warning message'); log.error('Error message');
Result
Debug messages show on console only; warnings and errors also get saved to app.log file.
Controlling log levels and outputs helps balance detail and performance, and keeps important info saved.
6
AdvancedGraceful Shutdown with Error Handling
🤔Before reading on: do you think programs should just stop on errors or try to clean up first? Commit to your answer.
Concept: Handle errors during shutdown to close resources and save state safely.
In real apps, you want to catch errors during shutdown to avoid data loss. Example: import { deferred } from 'https://deno.land/std@0.203.0/async/deferred.ts'; const shutdown = deferred(); async function cleanup() { try { console.log('Closing database connection...'); // simulate async cleanup await new Promise((r) => setTimeout(r, 1000)); console.log('Cleanup done'); } catch (error) { console.error('Cleanup error:', error.message); } finally { shutdown.resolve(); } } Deno.addSignalListener('SIGINT', () => { console.log('Received SIGINT, shutting down...'); cleanup(); }); await shutdown;
Result
On Ctrl+C, the program cleans up resources before exiting, handling any errors during cleanup.
Graceful shutdown with error handling prevents resource leaks and keeps data safe in production.
7
ExpertStructured Logging and Contextual Information
🤔Before reading on: do you think plain text logs are enough for complex apps, or is structured logging better? Commit to your answer.
Concept: Use structured logs with JSON and add context like request IDs to improve debugging and monitoring.
Structured logging means logs are data objects, not just text. Example: import * as log from 'https://deno.land/std@0.203.0/log/mod.ts'; await log.setup({ handlers: { console: new log.handlers.ConsoleHandler('INFO', { formatter: (rec) => JSON.stringify({ level: rec.levelName, msg: rec.msg, time: rec.datetime.toISOString(), requestId: rec.requestId || 'none' }), }), }, loggers: { default: { level: 'INFO', handlers: ['console'], }, }, }); log.info('User logged in', { requestId: 'abc123' });
Result
Logs output JSON objects with extra info, making it easier to filter and analyze logs automatically.
Structured logging with context is essential for diagnosing issues in distributed or complex systems.
Under the Hood
Deno's error handling relies on JavaScript's try-catch mechanism, which works by wrapping code execution and catching exceptions thrown during runtime. For asynchronous code, try-catch works with async/await syntax by pausing execution until promises settle. Logging in Deno uses handlers that format and output log records to destinations like console or files. The logging module manages loggers, handlers, and levels to filter and route messages efficiently.
Why designed this way?
Deno builds on JavaScript's native error handling to keep things simple and familiar for developers. The logging module is designed to be modular and configurable, allowing developers to choose where and how logs appear. This design balances ease of use with flexibility, avoiding complex setups while supporting production needs.
┌───────────────┐
│   Code Runs   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│  try { block }│
└──────┬────────┘
       │
       ▼
┌───────────────┐       ┌───────────────┐
│  Success     │       │  Error Thrown │
│  Continue    │       │  catch(error) │
└───────────────┘       └──────┬────────┘
                                │
                                ▼
                      ┌─────────────────┐
                      │  Log Handler    │
                      │  Formats & Saves│
                      └─────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does a try-catch block catch errors inside asynchronous callbacks automatically? Commit to yes or no.
Common Belief:Try-catch blocks catch all errors, including those inside asynchronous callbacks without extra handling.
Tap to reveal reality
Reality:Try-catch only catches errors in synchronous code or async/await. Errors inside callbacks or promises without await are not caught by surrounding try-catch.
Why it matters:Assuming try-catch catches all async errors leads to unhandled promise rejections and crashes in production.
Quick: Is logging only useful for errors, or should it include normal events? Commit to your answer.
Common Belief:Logging is only for errors and problems.
Tap to reveal reality
Reality:Logging normal events like startup, user actions, and warnings is crucial for understanding app behavior and diagnosing issues.
Why it matters:Ignoring non-error logs makes debugging harder and hides important context about app state.
Quick: Can you safely ignore errors if you log them? Commit to yes or no.
Common Belief:Logging an error means you can ignore it and continue without consequences.
Tap to reveal reality
Reality:Logging does not fix errors; some errors require proper handling or recovery to avoid bugs or crashes.
Why it matters:Ignoring errors after logging can cause silent failures and unstable apps.
Quick: Are plain text logs always enough for production debugging? Commit to yes or no.
Common Belief:Plain text logs are sufficient for all debugging needs.
Tap to reveal reality
Reality:Structured logs with context and metadata are essential for complex systems and automated analysis.
Why it matters:Relying on plain text logs limits your ability to quickly find and fix issues in large or distributed apps.
Expert Zone
1
Log handlers can be chained and filtered by level, allowing fine control over what gets logged where, which many overlook.
2
Errors thrown inside finally blocks or during async cleanup can bypass normal error handling, causing hidden bugs.
3
Contextual information like request IDs or user IDs in logs is critical for tracing issues in concurrent or distributed environments.
When NOT to use
Avoid heavy logging or complex error handling in simple scripts or prototypes where performance and simplicity matter more. Instead, use minimal console logs and basic try-catch. For large-scale apps, consider external monitoring tools like Sentry or Datadog for advanced error tracking.
Production Patterns
In production, use structured logging with JSON output sent to centralized log management systems. Implement global error handlers to catch uncaught exceptions and promise rejections. Use custom error classes to differentiate error types and handle them accordingly. Graceful shutdown handlers ensure resources close properly on errors or signals.
Connections
Observability in Distributed Systems
Builds-on
Understanding error handling and logging is foundational for observability, which combines logs, metrics, and traces to monitor complex systems.
Human Factors in Safety Engineering
Similar pattern
Just like error handling and logging prevent and record failures in software, safety engineering uses error detection and incident logging to improve human-machine system safety.
Incident Response in Cybersecurity
Builds-on
Effective logging provides the data needed for incident response teams to investigate and respond to security breaches quickly.
Common Pitfalls
#1Catching errors but not handling them properly
Wrong approach:try { const data = JSON.parse(input); } catch (error) { console.log(error); // no recovery or rethrow } // program continues as if nothing happened
Correct approach:try { const data = JSON.parse(input); } catch (error) { console.error('Invalid input:', error.message); // handle error or stop execution throw error; }
Root cause:Misunderstanding that logging alone fixes errors; proper handling or stopping is needed to avoid corrupted state.
#2Using console.log instead of structured logging
Wrong approach:console.log('User logged in at', new Date());
Correct approach:import * as log from 'https://deno.land/std@0.203.0/log/mod.ts'; log.info('User logged in', { timestamp: new Date().toISOString() });
Root cause:Not realizing that structured logs enable better filtering, searching, and integration with monitoring tools.
#3Ignoring unhandled promise rejections
Wrong approach:async function fetchData() { fetch('https://bad.url').then(res => res.json()); } fetchData();
Correct approach:async function fetchData() { try { const res = await fetch('https://bad.url'); const data = await res.json(); } catch (error) { console.error('Fetch failed:', error.message); } } fetchData();
Root cause:Not using await or catch on promises causes silent failures and crashes.
Key Takeaways
Error handling in Deno uses try-catch blocks to keep programs running safely when problems occur.
Logging records important events and errors, helping developers understand and fix issues faster.
Async errors require try-catch with async/await to be caught properly and avoid crashes.
Custom error classes and structured logging improve clarity and maintainability in complex apps.
Graceful shutdown and contextual logs are essential for reliable production systems.

Practice

(1/5)
1. What is the main purpose of using try...catch in Deno?
easy
A. To handle errors and keep the program running smoothly
B. To speed up the program execution
C. To create new files automatically
D. To log messages to the console

Solution

  1. Step 1: Understand the role of try...catch

    The try...catch block is used to catch errors that happen during program execution.
  2. Step 2: Identify the effect on program flow

    By catching errors, the program can handle them gracefully and continue running instead of crashing.
  3. Final Answer:

    To handle errors and keep the program running smoothly -> Option A
  4. Quick Check:

    Error handling = To handle errors and keep the program running smoothly [OK]
Hint: Try...catch blocks catch errors to prevent crashes [OK]
Common Mistakes:
  • Thinking try...catch speeds up code
  • Confusing error handling with logging
  • Believing try...catch creates files
2. Which of the following is the correct syntax to log an error message in Deno?
easy
A. console.log.error('An error occurred')
B. console.error('An error occurred')
C. console.err('An error occurred')
D. console.errorMessage('An error occurred')

Solution

  1. Step 1: Recall the console methods in Deno

    Deno uses standard JavaScript console methods like console.error() to log error messages.
  2. Step 2: Check each option's syntax

    Only console.error('An error occurred') is valid syntax for logging errors.
  3. Final Answer:

    console.error('An error occurred') -> Option B
  4. Quick Check:

    console.error() logs errors = console.error('An error occurred') [OK]
Hint: Use console.error() to log errors clearly [OK]
Common Mistakes:
  • Using non-existent console methods
  • Mixing console.log and error methods incorrectly
  • Misspelling console.error
3. What will be the output of this Deno code?
try {
  throw new Error('Oops!')
} catch (e) {
  console.log('Caught:', e.message)
}
medium
A. Caught: Oops!
B. Error: Oops!
C. Caught: undefined
D. No output

Solution

  1. Step 1: Understand the throw and catch behavior

    The code throws an error with message 'Oops!'. The catch block catches it and accesses e.message.
  2. Step 2: Analyze the console.log output

    The catch block logs 'Caught:' followed by the error message, so output is 'Caught: Oops!'.
  3. Final Answer:

    Caught: Oops! -> Option A
  4. Quick Check:

    Catch logs error message = Caught: Oops! [OK]
Hint: Catch block logs error.message after throw [OK]
Common Mistakes:
  • Expecting full error object printed
  • Confusing error message with undefined
  • Thinking no output occurs
4. Identify the error in this Deno code snippet:
try {
  const data = JSON.parse('{ bad json }')
} catch {
  console.log('Parsing failed', e.message)
}
medium
A. console.log should be console.error
B. JSON.parse syntax is correct
C. Missing error parameter in catch block
D. try block should not have code

Solution

  1. Step 1: Check catch block syntax

    In Deno (and modern JavaScript), catch block can omit error parameter, but to access error details, it must be declared.
  2. Step 2: Identify the problem with missing error parameter

    The code tries to catch error but does not declare error variable, so error details can't be accessed if needed.
  3. Final Answer:

    Missing error parameter in catch block -> Option C
  4. Quick Check:

    Catch needs error param to access error = Missing error parameter in catch block [OK]
Hint: Always include error param in catch to access error info [OK]
Common Mistakes:
  • Assuming catch() always requires error param
  • Thinking console.log is wrong for errors
  • Believing try block can't have code
5. You want to log an error with a timestamp and the error message in Deno. Which code snippet correctly does this?
hard
A. try { throw new Error('Fail') } catch (e) { console.log('Error:', e) }
B. try { throw new Error('Fail') } catch (e) { console.error(e) console.log(new Date()) }
C. try { throw new Error('Fail') } catch { console.error('Error occurred') }
D. try { throw new Error('Fail') } catch (e) { console.error(`[${new Date().toISOString()}] Error: ${e.message}`) }

Solution

  1. Step 1: Understand the requirement for timestamp and message

    The log must include current time in ISO format and the error message.
  2. Step 2: Check each option for correct formatting

    try { throw new Error('Fail') } catch (e) { console.error(`[${new Date().toISOString()}] Error: ${e.message}`) } uses template literals to combine timestamp and error message in one console.error call, which is correct.
  3. Final Answer:

    try { throw new Error('Fail') } catch (e) { console.error(`[${new Date().toISOString()}] Error: ${e.message}`) } -> Option D
  4. Quick Check:

    Timestamp + error message in one log = try { throw new Error('Fail') } catch (e) { console.error(`[${new Date().toISOString()}] Error: ${e.message}`) } [OK]
Hint: Use template literals with new Date().toISOString() for timestamps [OK]
Common Mistakes:
  • Logging error and timestamp separately
  • Missing error parameter in catch
  • Using console.log instead of console.error for errors
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard