Open jpkrohling opened 1 year ago
+1
I tried with the New-service command you provided above and it failed. I searched and found http://nssm.cc/, I tried running with this and it worked.
It doesnt work. The only option that seems to work is using NSSM... can't believe this. I've opened a new question about this topic. If promtail binary can't run by itself as a windows service, how should i run? manually?
I wanted to share feedback I heard from the community on the Grafana Brazil Telegram channel (https://t.me/grafanabr): installing Promtail on a Windows host needs to be better documented and more user-friendly. Users expect an installer to exist, installing both service configuration and the binary. Without that, users have to figure out how to place the binary under a service by themselves. A temporary remedy is to provide official instructions on how to create those services. I have not tested this myself, as I don't have a Windows host, but here's a set of commands that would work for this:
New-Item -Path "c:\" -Name "promtail" -ItemType "directory" $promtail_url = "https://github.com/grafana/loki/releases/download/v2.8.1/promtail-windows-amd64.exe.zip" $windows_dir = "c:\promtail\" $client = New-Object System.Net.WebClient $client.DownloadFile($promtail_url, $windows_dir) Expand-Archive -LiteralPath "c:\promtail\promtail-windows-amd64.exe.zip" -DestinationPath "c:\promtail" # Before running the next command, copy the config to "c:\promtail\promtail-local-config.yaml" New-Service -Name "Promtail" -DisplayName = "Monitoracao de Logs" -StartupType = "Automatic" -BinaryPathName "C:\promtail\\promtail-windows-amd64.exe --config.file=promtail-local-config.yaml"
This suggestion works in creating the service. However, when trying to start the service it fails. In the windows event I see error message about reaching a timeout and the service was unresponsive. When using NSSM to create the service it creates the service that the system can actually start and stop. For a dev environment I don't mind having a wrapper since we are still evaluating this agent. But the lack of documentation to setup the agent in a windows server makes it less attractive. I hope someone can post an actual config/steps on how to setup the agent as a service in windows.
I am also facing the same issue while trying to evaluate loki as a solution. has anyone found a way around this??
+1
news?
Part of the reason why I added the help wanted
flag to this issue is that I don't think we have anyone on the team who is running Windows. This information is going to have to be added by someone more familiar with Windows.
I have not tried with promtail till now, but am currently evaluating some exporters for windows that I run as scheduled tasks (found this on evaluating the windows_exporter.it remembering exactly where). I am running this approach since a few days on a test system, not sure if its a stable option. At least its restarting on reboot. May be its worth a try... I can provide a PS script for creating the task when i am back at work, if someone s in need
UPDATED VERSION based on feedback.
I've prepared a script for Promtail Windows Service installation, with download progress bar, ensure of run folder, default config and everything.. Give it a try.
Install-PromtailOnWindows.ps1
#############################################################################################
# This script downloads, if necessary, Promtail and its WinSW (Windows Service wrapper),
# creates default configuration and creates Windows service.
# It's a decision based script.
#
# ↓ ↓ ↓ ↓ HELPER FUNCTIONS ↓ ↓ ↓ ↓
# ↓↓↓↓↓↓↓ PROCESSING CODE ↓↓↓↓↓↓↓
#############################################################################################
function Prompt-User {
param(
[string]$Prompt,
[object]$Default
)
if (-not [string]::IsNullOrEmpty($Default)) {
if ($Default -is [bool]) {
$Prompt += " [$(if ($Default) {'True'} else {'False'})]"
}
else {
$Prompt += " [$Default]"
}
}
$input = Read-Host -Prompt $Prompt
if ([string]::IsNullOrEmpty($input)) {
$input = $Default
}
else {
if ($Default -is [bool]) {
$input = [bool]::Parse($input)
}
elseif ($Default -is [int]) {
$input = [int]::Parse($input)
}
elseif ($Default -is [string]) {
# No conversion needed for string
}
else {
throw "Unsupported default value type: $($Default.GetType().FullName)"
}
if ($input.GetType() -ne $Default.GetType()) {
throw "Entered value type doesn't match default value type"
}
}
return $input
}
function Ensure-Directory {
param(
[string]$Path
)
if (-not [System.IO.Directory]::Exists($Path)) {
[System.IO.Directory]::CreateDirectory($Path) | Out-Null
}
}
function Download-File {
param (
# Url of file to download
[Parameter(Mandatory)]
[string]$URL,
# Parameter help description
[Parameter(Mandatory)]
[string]$File
)
Begin {
function Show-Progress {
param (
# Enter total value
[Parameter(Mandatory)]
[Single]$TotalValue,
# Enter current value
[Parameter(Mandatory)]
[Single]$CurrentValue,
# Enter custom progresstext
[Parameter(Mandatory)]
[string]$ProgressText,
# Enter value suffix
[Parameter()]
[string]$ValueSuffix,
# Enter bar lengh suffix
[Parameter()]
[int]$BarSize = 40,
# show complete bar
[Parameter()]
[switch]$Complete
)
# calc %
$percent = $CurrentValue / $TotalValue
$percentComplete = $percent * 100
if ($ValueSuffix) {
$ValueSuffix = " $ValueSuffix" # add space in front
}
if ($psISE) {
Write-Progress "$ProgressText $CurrentValue$ValueSuffix of $TotalValue$ValueSuffix" -id 0 -percentComplete $percentComplete
}
else {
# build progressbar with string function
$curBarSize = $BarSize * $percent
$progbar = ""
$progbar = $progbar.PadRight($curBarSize, [char]9608)
$progbar = $progbar.PadRight($BarSize, [char]9617)
if (!$Complete.IsPresent) {
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($CurrentValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
}
else {
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($TotalValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
}
}
}
}
Process {
try {
$storeEAP = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# invoke request
$request = [System.Net.HttpWebRequest]::Create($URL)
$response = $request.GetResponse()
if ($response.StatusCode -eq 401 -or $response.StatusCode -eq 403 -or $response.StatusCode -eq 404) {
throw "Remote file either doesn't exist, is unauthorized, or is forbidden for '$URL'."
}
if ($File -match '^\.\\') {
$File = Join-Path (Get-Location -PSProvider "FileSystem") ($File -Split '^\.')[1]
}
if ($File -and !(Split-Path $File)) {
$File = Join-Path (Get-Location -PSProvider "FileSystem") $File
}
if ($File) {
$fileDirectory = $([System.IO.Path]::GetDirectoryName($File))
if (!(Test-Path($fileDirectory))) {
[System.IO.Directory]::CreateDirectory($fileDirectory) | Out-Null
}
}
[long]$fullSize = $response.ContentLength
$fullSizeMB = $fullSize / 1024 / 1024
# define buffer
[byte[]]$buffer = new-object byte[] 1048576
[long]$total = [long]$count = 0
# create reader / writer
$reader = $response.GetResponseStream()
$writer = new-object System.IO.FileStream $File, "Create"
# start download
$finalBarCount = 0 #show final bar only one time
do {
$count = $reader.Read($buffer, 0, $buffer.Length)
$writer.Write($buffer, 0, $count)
$total += $count
$totalMB = $total / 1024 / 1024
if ($fullSize -gt 0) {
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB"
}
if ($total -eq $fullSize -and $count -eq 0 -and $finalBarCount -eq 0) {
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB" -Complete
$finalBarCount++
#Write-Host "$finalBarCount"
}
} while ($count -gt 0)
}
catch {
$ExeptionMsg = $_.Exception.Message
Write-Host "Download breaks with error : $ExeptionMsg"
}
finally {
# cleanup
if ($reader) { $reader.Close() }
if ($writer) { $writer.Flush(); $writer.Close() }
$ErrorActionPreference = $storeEAP
[GC]::Collect()
Write-Host
}
}
}
function New-DefaultConfig {
param (
# Parameter help description
[Parameter(Mandatory)]
[string]$fullConfigPath,
[Parameter(Mandatory)]
[string]$runPath
)
Write-Output "Writing default config to $fullConfigPath"
$positionsFullpath = $runPath + "\positions.yaml"
$content = "
# 1. Update positions.yaml path
# 2. Update client's url - this is the url of Loki service - update or remove basic_auth
# 3. Update what logs should be scraped
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: $positionsFullpath
clients:
- url: https://[loki-url]:3100/loki/api/v1/push
basic_auth:
username: [loki]
password: [loki password]
scrape_configs:
- job_name: winlogs
static_configs:
- targets:
- localhost
labels:
host: [hostname]
job: [winlogs]
#NOTE: Need to be modified to scrape any additional logs of the system.
__path__: C:\Logs\*.log
"
Set-Content -Path $fullConfigPath -Value $content
}
function New-DefaultWinSWConfig {
param (
# Parameter help description
[Parameter(Mandatory)]
[string]$fullConfigPath,
[Parameter(Mandatory)]
[string]$fullPromtailBinPath,
[Parameter(Mandatory)]
[string]$fullPromtailConfigPath,
[Parameter(Mandatory)]
[string]$serviceName,
[Parameter(Mandatory)]
[string]$serviceDisplayName
)
Write-Output "Writing default WinSW config to $fullConfigPath"
$content = "
<!--
You can find more information about the configuration options here: https://github.com/kohsuke/winsw/blob/master/doc/xmlConfigFile.md
Full example: https://github.com/kohsuke/winsw/blob/master/examples/sample-allOptions.xml
-->
<service>
<!-- ID of the service. It should be unique across the Windows system-->
<id>$serviceName</id>
<!-- Display name of the service -->
<name>$serviceDisplayName</name>
<!-- Service description -->
<description>Starts a local Promtail service and scrapes logs according to configuration file: $fullPromtailConfigPath</description>
<!-- Path to the executable, which should be started -->
<executable>""$fullPromtailBinPath""</executable>
<arguments>--config.file=""$fullPromtailConfigPath""</arguments>
</service>
"
Set-Content -Path $fullConfigPath -Value $content
}
#############################################
# ↑ ↑ ↑ ↑ HELPER FUNCTIONS ↑ ↑ ↑ ↑
#
#
# ↓ ↓ ↓ ↓ PROCESSING CODE ↓ ↓ ↓ ↓
#############################################
Write-Warning -Message "This script creates a Window Service for Promtail log scraper. It is necessary to run it with Admin priviledges.
It can download necessary files from the Internet, but you can also put already downloaded files directly to proper directories."
$downloadUrl = "https://github.com/grafana/loki/releases/download/v2.9.5/promtail-windows-amd64.exe.zip"
$downloadWinSWUrl = "https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe"
$winSWFilename = "WinSW-x64.exe"
$binFilename = "promtail-windows-amd64.exe"
$configFilename = "promtail.yml"
$winSWConfigFilename = "WinSW-x64.xml"
$runPath = Prompt-User -Prompt "Run directory" -Default "C:\Promtail"
$fullBinPath = Join-Path -Path $runPath -ChildPath $binFilename
$fullWinSWBinPath = Join-Path -Path $runPath -ChildPath $winSWFilename
$downloadWinSWPath = $runPath
$shouldDownloadPromtail = Prompt-User -Prompt "Should we download Promtail?" -Default $true
if ($shouldDownloadPromtail) {
$downloadUrl = Prompt-User -Prompt "Download url" -Default $downloadUrl
$downloadPath = Prompt-User -Prompt "Download directory" -Default $runPath
Ensure-Directory -Path $downloadPath
$filename = $downloadUrl.Split("/")[-1]
$fullPath = Join-Path -Path $downloadPath -ChildPath $filename
Write-Host "Downloading archive..."
Download-File -URL $downloadUrl -File $fullPath
Write-Host "Expanding archive..."
Expand-Archive -LiteralPath $fullPath -DestinationPath $runPath -Force
}
else {
if (-not [System.IO.File]::Exists($fullBinPath)) {
throw "Could not find $fullBinPath"
}
}
$shouldCreateConfig = Prompt-User -Prompt "Create default promtail.yml config?" -Default $true
$configFullpath = Join-Path -Path $runPath -ChildPath $configFilename
if ($shouldCreateConfig) {
New-DefaultConfig -fullConfigPath $configFullpath -runPath $runPath
}
else {
$configFullpath = Prompt-User -Prompt "Promtail configuration file path" -Default $configFullpath
if (-not [System.IO.File]::Exists($configFullpath)) {
throw "Could not find $configFullpath"
}
}
$shouldCreateService = Prompt-User -Prompt "Create Promtail Windows service?" -Default $false
if ($shouldCreateService) {
$shouldDownloadWinSWUrl = Prompt-User -Prompt "Should we download Windows Service wrapper (WinSWUrl)?" -Default $true
if ($shouldDownloadWinSWUrl) {
$downloadUrl = Prompt-User -Prompt "Download url" -Default $downloadWinSWUrl
$downloadWinSWPath = Prompt-User -Prompt "Download directory" -Default $runPath
Ensure-Directory -Path $downloadPath
$filename = $winSWFilename
$fullWinSWBinPath = Join-Path -Path $downloadWinSWPath -ChildPath $filename
Write-Host "Downloading WinSW exe file..."
Download-File -URL $downloadUrl -File $fullWinSWBinPath
}
else {
if (-not [System.IO.File]::Exists($fullWinSWBinPath)) {
throw "Could not find $fullWinSWBinPath"
}
}
$winSWconfigFullpath = Join-Path -Path $downloadWinSWPath -ChildPath $winSWConfigFilename
$shouldCreateWinSWConfig = Prompt-User -Prompt "Create WinSW config as $winSWconfigFullpath ?" -Default $true
if ($shouldCreateWinSWConfig) {
$serviceName = Prompt-User -Prompt "Service name" -Default "Promtail"
$serviceDisplayName = Prompt-User -Prompt "Service name" -Default "Promtail Logs scraper"
New-DefaultWinSWConfig -fullConfigPath $winSWconfigFullpath -fullPromtailBinPath $fullBinPath -fullPromtailConfigPath $configFullpath -serviceName $serviceName -serviceDisplayName $serviceDisplayName
}
else {
if (-not [System.IO.File]::Exists($winSWconfigFullpath)) {
throw "Could not find $winSWconfigFullpath"
}
}
Write-Host "Installing Promtail Windows Service"
Start-Process -FilePath $fullWinSWBinPath -ArgumentList @("install") -NoNewWindow
Write-Host "Promtail Windows Service Installed (hopefully)"
}
WinSW-x64.xml
file now has paths in quotes just to be sure you don't run into issues when using spaces in paths.
Btw. to remove the service, you could use Remove-Service but that is available only from PowerShell 6 and above. If you have it you can run
Remove-Service -Name Promtail
https://github.com/grafana/loki/assets/6738956/13789456-69db-4449-8b84-70b6b8cf4302
@OndrejValenta Nice script! I didn't run it but I did try the following code, and with no avail, the service still will not start:
$params = @{
Name = "Promtail"
BinaryPathName = "C:\Promtail\promtail-windows-amd64.exe --config.file=C:\Promtail\promtail-config.yml"
DisplayName = "Promtail"
StartupType = "Automatic"
Description = "Starts a local Promtail service and scrapes logs"
}
New-Service @params
@OndrejValenta Nice script! I didn't run it but I did try the following code, and with no avail, the service still will not start:
$params = @{ Name = "Promtail" BinaryPathName = "C:\Promtail\promtail-windows-amd64.exe --config.file=C:\Promtail\promtail-config.yml" DisplayName = "Promtail" StartupType = "Automatic" Description = "Starts a local Promtail service and scrapes logs" } New-Service @params
Yes, I have to rework it. As it turns out, promtail is not ready to be a Windows Service on it's own and has to be wrapped with Windows Service Wrapper
This is an example of v2 configuration file named WinSW-x64.xml
(as it must be called in WinSW v2), which is sitting right next to WinSW-x64.exe
.
When you run it, it will create log files so you can see promtail output.
To install the service WinSW-x64.exe install
in the folder where xml/exe files are
<service>
<!-- ID of the service. It should be unique across the Windows system-->
<id>Promtail</id>
<!-- Display name of the service -->
<name>Promtail Logs scraper</name>
<!-- Service description -->
<description>Starts a local Promtail service and scrapes logs</description>
<!-- Path to the executable, which should be started -->
<executable>H:\Programs\Promtail\promtail-windows-amd64.exe</executable>
<arguments>--config.file=.\promtail.yml</arguments>
</service>
Yes, I have to rework it. As it turns out, promtail is not ready to be a Windows Service on it's own and has to be wrapped with Windows Service Wrapper
This is an example of v2 configuration file named
WinSW-x64.xml
(as it must be called in WinSW v2), which is sitting right next toWinSW-x64.exe
.When you run it, it will create log files so you can see promtail output.
To install the service
WinSW-x64.exe install
in the folder where xml/exe files are<service> <!-- ID of the service. It should be unique across the Windows system--> <id>Promtail</id> <!-- Display name of the service --> <name>Promtail Logs scraper</name> <!-- Service description --> <description>Starts a local Promtail service and scrapes logs</description> <!-- Path to the executable, which should be started --> <executable>H:\Programs\Promtail\promtail-windows-amd64.exe</executable> <arguments>--config.file=.\promtail.yml</arguments> </service>
Thank you for acknowledging and providing an alternate solution. Your input is greatly appreciated!
Yes, I have to rework it. As it turns out, promtail is not ready to be a Windows Service on it's own and has to be wrapped with Windows Service Wrapper This is an example of v2 configuration file named
WinSW-x64.xml
(as it must be called in WinSW v2), which is sitting right next toWinSW-x64.exe
. When you run it, it will create log files so you can see promtail output. To install the serviceWinSW-x64.exe install
in the folder where xml/exe files are<service> <!-- ID of the service. It should be unique across the Windows system--> <id>Promtail</id> <!-- Display name of the service --> <name>Promtail Logs scraper</name> <!-- Service description --> <description>Starts a local Promtail service and scrapes logs</description> <!-- Path to the executable, which should be started --> <executable>H:\Programs\Promtail\promtail-windows-amd64.exe</executable> <arguments>--config.file=.\promtail.yml</arguments> </service>
Thank you for acknowledging and providing an alternate solution. Your input is greatly appreciated!
Hi,
First, thanks to all the contributors of this topic !
To complete the answers above, for the WinSW config, note that if a path contains spaces, you have to double quote it ; you also may want to add a log parameter (cf WinSW doc). That gives the following example config :
<service>
<!-- ID of the service. It should be unique across the Windows system -->
<id>promtail_agent</id>
<!-- Display name of the service -->
<name>Promtail Agent</name>
<!-- Service description -->
<description>Log centralization with Promtail Agent</description>
<!-- Path to the executable, which should be started -->
<executable>"C:\Program Files\Promtail\promtail-windows-amd64.exe"</executable>
<arguments>--config.file="C:\Program Files\Promtail\promtail-config.yml"</arguments>
<!-- Defines logging mode for logs produced by the executable -->
<log mode="roll"></log>
</service>
Concerning the Promtail config, note that if you want to scrape more than one Windows event logs channel, a easy way to do it is to define a job for each channel (cf https://github.com/grafana/loki/issues/5905#issuecomment-1099352713).
To complete the answers above, for the WinSW config, note that if a path contains spaces, you have to double quote it ; you also may want to add a log parameter (cf WinSW doc). That gives the following example config :
I have updated the script to do all work necessary. Downloads WinSW wrapper, Promtail, and so on. Also, default WinSW XML contains paths in quotes.
Thank you very much @OndrejValenta ! It works as a charm. I don't know if it was a mistake of my own but I had to rewrite the URL https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe to download winsw when asked by the script. Otherwise, nothing else to share. You saved me several hours of work !
Thank you very much @OndrejValenta ! It works as a charm. I don't know if it was a mistake of my own but I had to rewrite the URL https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe to download winsw when asked by the script. Otherwise, nothing else to share. You saved me several hours of work !
Interesting, I've just tried https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe and it works just fine. They are going to release v3 soon anyway and the script will have to be updated accordingly. :-)
Enjoy.
I wanted to share feedback I heard from the community on the Grafana Brazil Telegram channel (https://t.me/grafanabr): installing Promtail on a Windows host needs to be better documented and more user-friendly. Users expect an installer to exist, installing both service configuration and the binary. Without that, users have to figure out how to place the binary under a service by themselves. A temporary remedy is to provide official instructions on how to create those services. I have not tested this myself, as I don't have a Windows host, but here's a set of commands that would work for this: