Grafana Alloy - Labels

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 = {}
}