---
title: "PowerShell Write-Log: A Simple Logging Function Tutorial"
description: "Learn how to create a reusable PowerShell logging function to simplify tracking script activities. This tutorial shows you how to build a Write-Log function to generate timestamped log entries."
canonical: "https://adamtheautomator.com/powershell-write-log-tutorial/"
---

# PowerShell Write-Log: A Simple Logging Function Tutorial

> Learn how to create a reusable PowerShell logging function to simplify tracking script activities. This tutorial shows you how to build a Write-Log function to generate timestamped log entries.

Source: https://adamtheautomator.com/powershell-write-log-tutorial/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![PowerShell Write-Log: A Simple Logging Function Tutorial](https://adamtheautomator.com/wp-content/uploads/2024/12/featured-image-5.webp)

# PowerShell Write-Log: A Simple Logging Function Tutorial

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)25 December 20245 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [The Problem with Basic Logging](#the-problem-with-basic-logging)
*   [Building a Basic Write-Log Function](#building-a-basic-write-log-function)
*   [Adding More Functionality](#adding-more-functionality)
*   [Real-World Example: Software Installation Script](#real-world-example-software-installation-script)
*   [Useful Tips](#useful-tips)
*   [Conclusion](#conclusion)
*   [Log Rotation](#log-rotation)
*   [Different Output Formats (CSV, JSON)](#different-output-formats-csv-json)
*   [Network Path Support](#network-path-support)
*   [Email Notifications for Errors](#email-notifications-for-errors)

If you’re writing PowerShell scripts that do anything meaningful, you need logging. Whether you’re deploying software, managing services, or automating tasks, having a record of what your script did (or didn’t do) is crucial. In this tutorial, you’ll learn how to create a simple but effective PowerShell logging function.

## Prerequisites

If you’d like to follow along with this tutorial, be sure you have:

*   Windows 10 or Windows Server with PowerShell 5.1 or PowerShell 7+
*   A text editor (VSCode recommended)
*   Basic understanding of PowerShell functions

## The Problem with Basic Logging

Let’s say you’re writing a script to silently install some software. The basic approach might look something like this:

```powershell
Add-Content -Path "C:\\Scripts\\install.log" -Value "Starting install..."
Start-Process -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow
Add-Content -Path "C:\\Scripts\\install.log" -Value "Finished install."
```

This works, but it has some issues:

*   No timestamps
*   Repetitive code
*   Inconsistent logging format
*   Hard-coded log path

Let’s fix these problems by building a proper logging function.

## Building a Basic Write-Log Function

First, let’s create a simple function that adds timestamps to our log entries:

```powershell
function Write-Log {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Message
    )

    $timeGenerated = Get-Date -Format HH:mm:ss
    Add-Content -Path "C:\\Scripts\\script.log" -Value "$timeGenerated - $Message"
}
```

Now you can use it like this:

```powershell
Write-Log -Message "Starting install..."
Start-Process -FilePath 'installer.exe' -ArgumentList '/i /s' -Wait -NoNewWindow
Write-Log -Message "Finished install."
```

The log file (C:\\Scripts\\script.log) will contain entries that look like:

```
09:42:15 - Starting install...
09:43:22 - Finished install.
```

Much cleaner! But we can do better.

## Adding More Functionality

Let’s enhance our logging function with some useful features:

*   Custom log paths
*   Different log levels (Info, Warning, Error)
*   Date in filename
*   Error handling

Here’s the improved version:

```powershell
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$LogFilePath = "C:\\Scripts\\Logs",

        [Parameter()]
        [ValidateSet('Information','Warning','Error')]
        [string]$Level = "Information"
    )

    # Create the log directory if it doesn't exist
    if (!(Test-Path $LogFilePath)) {
        New-Item -Path $LogFilePath -ItemType Directory -Force | Out-Null
    }

    # Build the log file path with date
    $date = Get-Date -Format "MM-dd-yyyy"
    $logFile = Join-Path $LogFilePath "log-$date.txt"

    # Get the current timestamp
    $timeStamp = Get-Date -Format "HH:mm:ss"

    # Create the log entry
    $logEntry = "$timeStamp [$Level] - $Message"

    try {
        Add-Content -Path $logFile -Value $logEntry -ErrorAction Stop
    }
    catch {
        Write-Error "Failed to write to log file: $_"
    }
}
```

This enhanced version gives you much more flexibility. Here’s how to use it:

```
# Basic information logging
Write-Log -Message "Starting software installation"

# Warning about a non-critical issue
Write-Log -Message "Config file not found, using defaults" -Level Warning

# Log an error
Write-Log -Message "Installation failed!" -Level Error

# Use a custom log path
Write-Log -Message "Custom path log" -LogFilePath "D:\\CustomLogs"
```

The resulting log file (log-03-12-2024.txt) will look like this:

```
10:15:22 [Information] - Starting software installation
10:15:23 [Warning] - Config file not found, using defaults
10:15:25 [Error] - Installation failed!
```

And in D:\\CustomLogs\\log-03-12-2024.txt:

```
10:15:26 [Information] - Custom path log
```

Notice how each entry includes the timestamp, log level in brackets, and the message. This structured format makes it easy to parse logs and quickly identify issues.

## Real-World Example: Software Installation Script

Let’s put our logging function to work in a real script that installs software silently:

```powershell
# First, dot-source the logging function
. .\\Write-Log.ps1

# Script variables
$installer = "C:\\Installers\\software.exe"
$logPath = "C:\\Scripts\\InstallLogs"

# Start logging
Write-Log -Message "Beginning installation process" -LogFilePath $logPath

# Check if installer exists
if (Test-Path $installer) {
    Write-Log -Message "Found installer at: $installer"

    try {
        # Attempt installation
        Write-Log -Message "Starting installation..."
        $process = Start-Process -FilePath $installer -ArgumentList '/i /s' -Wait -NoNewWindow -PassThru

        # Check the exit code
        if ($process.ExitCode -eq 0) {
            Write-Log -Message "Installation completed successfully"
        }
        else {
            Write-Log -Message "Installation failed with exit code: $($process.ExitCode)" -Level Error
        }
    }
    catch {
        Write-Log -Message "Installation failed with error: $_" -Level Error
    }
}
else {
    Write-Log -Message "Installer not found at: $installer" -Level Error
}

Write-Log -Message "Installation script completed"
```

The resulting log file will look something like this:

```
09:15:22 [Information] - Beginning installation process
09:15:22 [Information] - Found installer at: C:\\Installers\\software.exe
09:15:22 [Information] - Starting installation...
09:16:45 [Information] - Installation completed successfully
09:16:45 [Information] - Installation script completed
```

## Useful Tips

Here are some best practices when using this logging function:

1.  **Always log the start and end of your script** – This helps track script execution time and completion status.
    
2.  **Use appropriate log levels** – Don’t mark everything as an error; use the right level for the situation:
    
    *   Information: Normal operations
    *   Warning: Non-critical issues
    *   Error: Critical problems that need attention
3.  **Include relevant details** – Log enough information to understand what happened:
    
    ```
    # Bad
    Write-Log "Failed to connect"
    
    # Good
    Write-Log "Failed to connect to server 'SQL01' - timeout after 30 seconds" -Level Error
    ```
    
4.  **Clean up old logs** – Consider adding log rotation to prevent filling up disk space:
    
    ```powershell
    # Delete logs older than 30 days
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
        Remove-Item
    ```
    

## Conclusion

A good logging function is essential for any serious PowerShell script. With the `Write-Log` function we’ve built, you now have a flexible and reusable way to add proper logging to all your scripts. Remember to adapt the function to your specific needs – you might want to add features like:

### Log Rotation

```powershell
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [int]$MaxLogFiles = 30  # Keep last 30 days of logs
    )

    # Remove old log files
    Get-ChildItem -Path $LogFilePath -Filter "*.txt" |
        Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$MaxLogFiles) } |
        Remove-Item -Force

    # Continue with normal logging...
}
```

### Different Output Formats (CSV, JSON)

```powershell
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [ValidateSet('TXT','CSV','JSON')]
        [string]$Format = 'TXT'
    )

    $logEntry = [PSCustomObject]@{
        Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        Level = $Level
        Message = $Message
    }

    switch ($Format) {
        'CSV'  { $logEntry | Export-Csv -Path "$LogFilePath\\log.csv" -Append -NoTypeInformation }
        'JSON' { $logEntry | ConvertTo-Json | Add-Content -Path "$LogFilePath\\log.json" }
        'TXT'  { "$($logEntry.Timestamp) [$($logEntry.Level)] - $($logEntry.Message)" |
                 Add-Content -Path "$LogFilePath\\log.txt" }
    }
}
```

### Network Path Support

```powershell
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [string]$NetworkPath = "\\\\server\\logs"
    )

    # Test network path connectivity
    if (!(Test-Path $NetworkPath)) {
        # Fallback to local logging if network is unavailable
        $NetworkPath = "C:\\Scripts\\Logs"
        Write-Warning "Network path unavailable. Using local path: $NetworkPath"
    }

    # Continue with normal logging...
}
```

### Email Notifications for Errors

```powershell
function Write-Log {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,

        [Parameter()]
        [string]$SmtpServer = "smtp.company.com",

        [Parameter()]
        [string[]]$NotifyOnError = "admin@company.com"
    )

    # Normal logging first...

    # Send email if this is an error
    if ($Level -eq 'Error' -and $NotifyOnError) {
        $emailParams = @{
            From = "powershell@company.com"
            To = $NotifyOnError
            Subject = "PowerShell Script Error"
            Body = "Error occurred at $timeStamp`n`nMessage: $Message"
            SmtpServer = $SmtpServer
        }

        try {
            Send-MailMessage @emailParams
        }
        catch {
            Write-Warning "Failed to send error notification: $_"
        }
    }
}
```

The key is to start with a solid foundation and build up from there based on your specific needs. These examples should give you a good starting point for extending the basic logging function with more advanced features.

For example, you might combine several of these features into a single, comprehensive logging solution:

```powershell
Write-Log -Message "Critical error in payment processing" `
          -Level Error `
          -Format CSV `
          -NetworkPath "\\\\server\\logs" `
          -NotifyOnError "admin@company.com","manager@company.com" `
          -MaxLogFiles 90
```

This would:

*   Log the error in CSV format
*   Store it on a network share
*   Email multiple recipients
*   Maintain 90 days of log history

Remember to test thoroughly, especially when implementing network paths or email notifications, as these external dependencies can affect your script’s reliability. Happy scripting!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-write-log-tutorial%2F&text=PowerShell%20Write-Log%3A%20A%20Simple%20Logging%20Function%20Tutorial)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-write-log-tutorial%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-write-log-tutorial%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
