I wanted to write my own Logging for my scripts.
But there is this smart sentences out there "Do not invent the wheel again"
So I was searching throug the web and found some solutions.
But they seem either to simple (like Write-Host into a csv) or to complicated for me to work with it. (Complex with error levels etc).
All I need for myself and my skill level is something like
Create File --> C:\test\log.txt
Write-Log "Removing file $item was successfull"
New-Item -Path "C:\test\" -Name "ServiceStopped.txt" -ItemType file
Function Write-Log
{
Param ([string]$logstring)
$timestamp = Get-Date -Format "dd-MM-yyyy HH:mm:ss"
$log = "[$timestamp]: $logstring"
$log >> "ServiceStopped.txt"
}
I would like to have an output like
[26.04.2019 12:40]: This has been done
[26.04.2019 12:40]: That has been done
[26.04.2019 12:40]: Working on $a
[26.04.2019 12:40]: Failed while working $b
EDIT:
After the friendly tipps in comments I got myself this
$Logpath = "C:\test\"
$logname = "ServiceStopped.txt"
New-Item -Path $Logpath -Name $logname -ItemType file
Function Write-Log
{
Param ([string]$logstring)
$timestamp = Get-Date -Format "dd-MM-yyyy HH:mm:ss"
$log = "[$timestamp]: $logstring"
Add-Content -Value $log -Path "$Logpath\$logname"
}
Seems to get the output like I wanted it
Looking further to mentiond $Error[0]
Related
I'm trying to get powershell to write information about the execution of an operation to a log file. There is a folder with logs, it is necessary that the script delete files older than a certain number of days from the folder (implemented) and record information about the deletion of files or the absence of files in the folder (in case of an error when executing the script). Now the script writes information to the log about the absence of files, then if they are. Tell me what to do wrong? The text is as follows:
if ($error) {"No files found" *>> "D\TEST\Log $(gat-date -f dd-MM-yyy).txt"}
else {"Files deleted" *>> "D\TEST\Log $(gat-date -f dd-MM-yyy).txt"}
In principle $error is a automatic variable containg all errors occured within the current session. From my point of view its a better approach to use try/catch to handle errors. By doing so you can specify the error message to write to the logfile that matches the error, instead of writing the same error message for any kind of errors e.g.:
try {
#do someting
"Did something successfully" | set-content -path $logfilePath -Append -Force -Confirm:$false
}
Catch {
#gets executed if a terminating error occured
write-error "Failed to do something, Exception: $_"
"Failed to do something, Exception: $_" | set-content -path $logfilePath -Append
}
But back to your example, you could do:
$date = get-date -Format dd-MM-yy
$logFilePath = "D\TEST\Log_$date.txt"
If ($error){
"No files found" | set-content -path $logfilePath -Append -Force -Confirm:$false
}
Else {
"Files deleted" | set-content -path $logfilePath -Append -Force -Confirm:$false
}
ok based on the comment you could go this route:
$date14daysBack = (get-date).AddDays(-14)
$date = Get-Date -Format dd-MM-yyyy
$LogfilePath = "D:\TEST\Log_$date.txt"
try {
#try to map drive, if something goes wrong stop processing. You do not have to escape the $ if you use single quotes
New-PSDrive -Name "E" -PSProvider "FileSystem" -Root '\\Computer\D$\TEST' -Persist -ErrorAction:Stop
}
Catch {
"Failed to map drive '\\Computer\D$\TEST' - Exception $_" | Set-Content -Path $logfilePath -Append -Force
throw $_
}
try {
#I replaced forfiles and the delete operation with the powershell equivalent
$files2Remove = get-childitem -path E:\ -recurse | ?{$_.LastWriteTime -ge $date14daysBack}
$null = remove-item -Confirm:$false -Force -Path $files2remove.FullName -ErrorAction:stop
}
Catch {
"Failed to delete files: $($files2remove.FullName -join ',') - Exception: $_" | Set-Content -Path $logfilePath -Append -Force
}
#If files were found
If ($files2Remove){
"Files deleted, count: $($files2remove.count)" | Set-Content -Path $logfilePath -Append -Force
}
Else {
"No files found" | Set-Content -Path $logfilePath -Append -Force
}
Currently the code does not filter for '.' like in your sample: /m . - do I understand this correctly the filename is only a dot, nothing else?
The script has earned in the following form:
#cleaning up errors
$Error.Clear()
#days storage
$int = 11
#connecting a network drive
New-PSDrive -Name "E" -PSProvider "FileSystem" -Root "\\Computer\D`$\TEST" -Persist
#deleting files
FORFILES /p E:\ /s /m *.* /d -$int /c "CMD /c del /Q #FILE"
#disabling a network drive
Remove-PSDrive E
#recording information in the log
$date = Get-Date -Format dd-MM-yyyy
$LogfilePath = "D:\TEST\Log_$date.txt"
(Get-Date) >> $LogfilePath
if ($Error){"No files found" | Add-content -path $LogfilePath -Force -Confirm:$false}
Else {"Files deleted" | Add-Content -path $LogfilePath -Force -Confirm:$false}
$dateTime = Get-Date -Format "yyyyMMdd"
$Logfile = $logfile + $dateTime +".log"
if((Get-ChildItem $Logfile).CreationTime.Date -ne (Get-Date).Date)
{
Write-Host "creating new"
New-Item -Path $Logfile -ItemType File -Force
}
else
{
Write-Host "existing"
}
## This function facilitates in capturing various events into a log file when the script will run
function WriteLog
{
param([string]$Message)
filter timestamp {"$(Get-Date -Format G) $_"}
$Message = $Message | timestamp
Add-content $Logfile -value $Message
}
I am using this small code which will create log file per day if doesn't exist. It append log messages whenever Writelog function is triggered.
Problem facing --> this is working as per expectations for maximum 4 consecutive runs and After this the script is running fine, but not appending any message to the logfile.
If it was me I would define your log path variable once and then use throughout your script. You seem to be trying to access the same name by different methods which will be prone to errors. Your Write-Log method for example should take two parameters: Message and LogFile.
For an easier way to create logs using a Transcript, I have a logs sub folder and I just top and tail my script with the following:
# Start logging
#######################################################################
$log_path = $PSScriptRoot | Join-Path -ChildPath 'Logs'
$script_name = $PSCommandPath -replace '^.*\\([^\\]+)\.ps1$', '$1'
$log_name = '{0}_{1:yyyyMMddhhmmss}.log' -f $script_name, (Get-Date)
$log = $log_path | Join-Path -ChildPath $log_name
Start-Transcript -Path $log -Append -IncludeInvocationHeader
# Do stuff
# Use Write-Host for log comments
# Captures errors without additional code
# Stop logging
#######################################################################
Stop-Transcript
I'm looking to create daily folders using a Powershell script, and while the first 2 folders are being created no problem, the third folder on my list isn't being created. Below is my code, and it's the Raw Data folder that's not being created for some reason - any suggestions as to why this may be happening?
$months = Get-Date -UFormat %b
$monthl = Get-Date -UFormat %B
$year = Get-Date -UFormat %Y
$timestamp = Get-Date -UFormat "%d%m%Y"
$folderstamp = Get-Date -UFormat "%d-%m-%Y"
mkdir "X:\Client Services & Fulfilment\Fulfilment\CMS\$year\$monthl $year\Investec_AML\$folderstamp"
mkdir "X:\Client Services & Fulfilment\Fulfilment\CMS\$year\$monthl $year\Investec_AML\$folderstamp\Final Output"
mkdir "X:\Client Services & Fulfilment\Fulfilment\CMS\$year\$monthl $year\Investec_AML\$folderstamp\Raw Data"
If I write out that line of code on Powershell itself, it returns a LastWriteTime date of 01/01/1601?! See screenshot link below. The mode seems to show all possible modes available?
powershell screenshot
From your screenshot, I can see the Raw Data folder indeed exists. Under Mode you can see its attributes:
d - Directory
a - Archive
r - Read-only
h - Hidden
s - System
l - Reparse point, symlink, etc.
Maybe you should investigate that folder (or link) some more to find out why it is there and if it is a symlink, where it points to.
Anyway, here's your code in more PowerShell style:
$now = Get-Date
$months = $now.ToString("MMM")
$monthl = $now.ToString("MMMM")
$year = $now.Year
$timestamp = $now.ToString("ddMMyyyy")
$folderstamp = $now.ToString("dd-MM-yyyy")
$folderName = "X:\Client Services & Fulfilment\Fulfilment\CMS\$year\$monthl $year\Investec_AML\$folderstamp"
try {
New-Item -ItemType Directory -Path $folderName -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path (Join-Path -Path $folderName -ChildPath 'Final Output') -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path (Join-Path -Path $folderName -ChildPath 'Raw Data') -ErrorAction Stop | Out-Null
}
catch {
Write-Error $_.Exception.Message
}
Hope that helps
For whatever the reason (the word Raw perhaps?), replacing the space in the new directory name with an underscore has worked i.e. 'Raw_Data' instead of 'Raw Data', so I will roll this out for other daily jobs we process that use a similar folder structure.
Thanks to Theo for the tidier code!
$now = Get-Date
$months = $now.ToString("MMM")
$monthl = $now.ToString("MMMM")
$year = $now.Year
$timestamp = $now.ToString("ddMMyyyy")
$folderstamp = $now.ToString("dd-MM-yyyy")
$folderName = "X:\Client Services & Fulfilment\Fulfilment\CMS\$year\$monthl $year\Investec_AML\$folderstamp"
try {
New-Item -ItemType Directory -Path $folderName -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path (Join-Path -Path $folderName -ChildPath 'Final_Output') -ErrorAction Stop | Out-Null
New-Item -ItemType Directory -Path (Join-Path -Path $folderName -ChildPath 'Raw_Data') -ErrorAction Stop | Out-Null
}
catch {
Write-Error $_.Exception.Message
}
I am trying to write the following script, which checks a folder for a set of files, if they exist, move them to an "archive" folder. If they don't, write an error message to screen and to a log file.
The files move fine, so the first part of the IF is working correctly, but if there are no files to move, the else should kick in and output the error....but it is not.
variables.ps1:
#----- define parameters -----#
#----- Treat All Errors as Terminating -----#
$ErrorActionPreference = "Stop"
#----- Set count to 0 -----#
$count = 0
#----- get current date ----#
$Now = Get-Date
#----- define amount of days ----#
$Days = "0"
#----- define folder where files are located ----#
$SourceFolder = "C:\HG1\Test\Files"
#----- define folder where files are to be moved to ----#
$DestFolder = "C:\HG1\Test\Files\Archive"
#----- define folder where files are to be moved to ----#
$LogPath = "C:\HG1\archive.log"
#----- define extension ----#
$Extension = "*.log"
#----- define LastWriteTime parameter based on $Days ---#
$LastWrite = $Now.AddDays(-$Days)
#----- get files based on lastwrite filter and specified folder ---#
$Files = Get-Childitem $SourceFolder -Include $Extension -Recurse | Where {$_.LastWriteTime -le "$LastWrite"}
archive_files.ps1
#----- Call variables file variables.ps1 - MUST BE IN SAME LOCATION AS SCRIPT ----#
. ./variables.ps1
foreach ($File in $Files)
{
if ($File -ne $NULL)
{
move-item -path $File.FullName -destination $DestFolder
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: Archived File $File")
}
else
{
write-host "ERROR: No files to archive" -ForegroundColor "Red"
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t ERROR: No files to archive")
}
}
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: ***Archiving script completed successfully***")
Any help would be greatly appreciated.
#----- Call variables file variables.ps1 - MUST BE IN SAME LOCATION AS SCRIPT ----#
. ./variables.ps1
if ($File)
{
foreach ($File in $Files)
{
move-item -path $File.FullName -destination $DestFolder
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: Archived File $File")
}
}
else
{
write-host "ERROR: No files to archive" -ForegroundColor "Red"
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t ERROR: No files to archive")
}
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: ***Archive script completed successfully***")
here,
Foreach each won't process for $null value
so include foreach if value is not null
if won't enter for $null value so " -ne $null is" not necessary
hopes this works for you,
Regards,
Kvprasoon
You should use the Test-Path cmdlet to check whether the file exist:
foreach ($File in $Files)
{
if (Test-Path $File)
{
move-item -path $File.FullName -destination $DestFolder
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: Archived File $File")
}
else
{
write-host "ERROR: No files to archive" -ForegroundColor "Red"
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t ERROR: No files to archive")
}
}
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: ***Archiving script completed successfully***")
You've got that logic backwards there.
Each $File in $Files will always be something, but the $Files collection itself may be empty:
if(-not $Files)
{
foreach($File in $Files)
{
Move-Item -Path $File.FullName -Destination $DestFolder
Add-Content $LogPath -Value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: Archived File $File")
}
}
else
{
Write-Host "ERROR: No files to archive" -ForegroundColor "Red"
Add-Content $LogPath -Value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t ERROR: No files to archive")
}
Add-Content $LogPath -value ("$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss')) `t INFO: ***Archiving script completed successfully***")
I have the following requirement for a script:
a. Get the Scripts Name and Path.
b. Create a ScriptPath\Log-Time|date\Logfile.Log
c. Give the user 3 options, depending on the input update the log file.
For the above requirement ive created the following script:
#Variables Declaration-------------------------------------------
$pat = Split-Path $script:MyInvocation.MyCommand.Path -parent
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$a = "[Info]:"+$LogTime+" Logged into Server"
$b = "[Warning]:"+$LogTime+" Unauthorized Access"
$c = "[Error]:"+$LogTime+" Wrong Credentials"
$ScriptName = $MyInvocation.MyCommand.Name
$path = $pat
#Folder and Log Creation------------------------------------------
if([IO.Directory]::Exists($path))
{
$m = New-Item -ItemType directory -Path $path -name Log
$n = New-Item -ItemType directory -Path $m -name $LogTime
}
$LogName = $ScriptName+"_Log_"+$LogTime+".log"
$log = New-Item -ItemType file -path $n -name $LogName
# Log Function------------------------------------------------------
log($in)
function log
{
$in = Read-Host "Enter your Option"
if ($in -eq "0")
{
$latest = Get-ChildItem -Path $n | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$p = $path+$latest.name
Add-Content $p -value $a
}
elseif ($in -eq "1")
{
$latest = Get-ChildItem -Path $n | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$p = $path+$latest.name
Add-Content $p -value $b
}
elseif ($in -eq "2")
{
$latest = Get-ChildItem -Path $n | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$p = $path+$latest.name
Add-Content $p -value $c
}
else
{
$o = "Invalid Input"
$o
}
Move-Item $p $n
}
Whenever i run this, i get two logfiles created.
Exec2.ps1_Log_04-04-2014_10-21-11.log &&&&
MExec2.ps1_Log_04-04-2014_10-21-11.log [M is the folder where the script is run]
and the first log file is empty while the second one contains the text.
Could anyone please help me fix this? and if possible make the script short and sweet somehow?
Thanks and Regards,
Kain
Some of your code seemed redundant, so I removed some. Such as defining $pat and then duplicating it into $path so I just defined $path once. I got rid of $a, $b, and $c since you can just as easily enter the value directly.
Then I got rid of the whole get the latest file in the log folder, and just added the content to the log file defined earlier. I changed the log entries so they were done by tabs so the entry type, date, and the actual entry all line up nicely when you view the log file.
Lastly I changed the whole If>ElseIf>ElseIf thing into a Switch, because Switch is awesome and totally underused! Oh yeah, I got rid of $o = "Invalid Input"; $o because it's pointless to define a variable and then just echo it. What you could do is in the default section have it write-host the error and call the log function again, so people are forced to enter a valid entry.
#Variables Declaration-------------------------------------------
$path = Split-Path $script:MyInvocation.MyCommand.Path -parent
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$ScriptName = $MyInvocation.MyCommand.Name
#Folder and Log Creation------------------------------------------
$m = New-Item -ItemType directory -Path $path -name Log
$n = New-Item -ItemType directory -Path $m -name $LogTime
$LogName = $ScriptName+"_Log_"+$LogTime+".log"
$log = New-Item -ItemType file -path $n -name $LogName
# Log Function------------------------------------------------------
log
function log{
$in = Read-Host "Enter your Option (0,1,2)"
Switch ($in){
0 {Add-Content $log -value ("[Info]:`t`t" + $LogTime + "`tLogged into Server")}
1 {Add-Content $log -value ("[Warning]:`t" + $LogTime + "`tUnauthorized Access")}
2 {Add-Content $log -value ("[Error]:`t" + $LogTime + "`tWrong Credentials")}
default {Write-Output "Invalid Input"}
}
}