Hello,
I’ve been battling with adding labels to my SFTP logs being ingested to Loki via Alloy. Can you please take a look at the config and advise where I’m going wrong?
I may be going over the top with labelling (I’m new to Grafana). My goal is to just have a dashboard with a live feed panel of our logs (formatted to only include Date/time, account name, IP and log message) & a failed uploads and downloads panel.
Any help would be much appreciated!
Local.file_match "sysax_logs" {
path_targets = [{
__address__ = "localhost",
__path__ = "/var/log/sftp/sysax/*.log",
job = "sysax",
source = "sysax",
}]
}
loki.process "sysax_logs" {
forward_to = [loki.write.default.receiver]
// 1 Parse the line header (timestamp + type + payload)
stage.regex {
expression = "^(?P<timestamp>\\d{2}/\\d{2}/\\d{4}\\s+\\d{2}:\\d{2}:\\d{2}\\s+(?:AM|PM)):\\s*\\[(?P<type>NOTE|EVNT|WARN)\\]\\s*(?P<payload>.+)$"
}
// 2 Use the log's own timestamp (Sysax uses local time with AM/PM)
stage.timestamp {
source = "timestamp"
format = "01/02/2006 03:04:05 PM" // Go time layout for AM/PM
location = "Europe/London"
}
// 3 NOTE: SSH login lines
stage.match {
selector = "{type=\"NOTE\"}"
stage.regex {
source = "payload"
expression = "^SSH Connection \\((?P<ip>\\d+\\.\\d+\\.\\d+\\.\\d+)\\) logged into account (?P<account>[A-Za-z0-9_\\-]+) \\(Auth: (?P<auth>[^)]+)\\)$"
}
stage.labels {
values = {
action = "LOGIN",
result = "OK",
}
}
}
// 4 NOTE: disconnects (optional ip label)
stage.match {
selector = "{type=\"NOTE\"}"
stage.regex {
source = "payload"
expression = "^Connection from (?P<disc_ip>\\d+\\.\\d+\\.\\d+\\.\\d+) disconnected$"
}
stage.labels {
values = {
ip = "disc_ip",
}
}
}
// 5 EVNT CSV payloads:
// "[EVNT] user,ip,protocol,auth,action,result,....,message"
stage.match {
selector = "{type=\"EVNT\"}"
stage.regex {
source = "payload"
// user,ip,protocol,auth,action,result,*,*,path,*,msg
expression = "^(?P<account>[^,]+),(?P<ip>[^,]+),(?P<protocol>[^,]+),(?P<auth>[^,]+),(?P<action>[^,]+),(?P<result>[^,]+),[^,]*,[^,]*,(?P<path>[^,]*),[^,]*,(?P<msg>.+)$"
}
stage.labels {
values = {
account = "account",
ip = "ip",
protocol = "protocol",
action = "action",
result = "result",
}
}
}
// 6) Low-cardinality base labels available on all lines
stage.labels {
values = {
type = "type",
}
}
}
loki.source.file "sysax_logs" {
targets = local.file_match.sysax_logs.targets
forward_to = [loki.process.sysax_logs.receiver]
legacy_positions_file = "/tmp/positions-sysax.yaml" // separate file (nice-to-have)
}
loki.write "default" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
external_labels = {}
}