All Products
Search
Document Center

Cloud Monitor:Go Template

Last Updated:Mar 26, 2026

The CMS notification enrichment component, a core component of the Alibaba Cloud monitoring and alerting system, enriches, renders, and distributes alert messages. Its dual-template-engine architecture maintains compatibility with legacy Velocity templates while supporting the more powerful Go template engine.

Template engines

  • Go template engine - Based on Go Template, this engine provides powerful features for creating custom templates.

  • Velocity template engine - Based on Apache Velocity, this engine supports legacy alert templates and is built into Cloud Monitor.

Data flow architecture

alert event → EnrichHandler → TemplateRenderHandler → MessageRenderHandler → notification distribution
    ↓             ↓                     ↓                       ↓                ↓
 Raw data    variable enrichment   annotation rendering    message rendering   Channel adaptation

Customizing alert content

Use Go Template syntax to define custom parameters within the alert content. User notifications will then include this custom content. Ensure that the Go Template syntax is correct.

For example, when you select a metric from a metric group, a built-in PromQL query and pre-configured templates for various notification channels are available. If a pre-configured template does not meet your needs, you can modify it. For example:

Node {{ $labels.instance }} CPU usage {{ $labels.metrics_params_opt }} {{ $labels.metrics_params_value }}%. Current CPU usage: {{ printf "%.2f" $value }}%

When rendering a template, Go Template populates it with data from the context. The namespace {{$labels.namespace}} and Pod {{$labels.pod_name}} are extracted from the PromQL query results.

You can also add custom labels and annotations. You can reference custom labels using {{$labels.custom_label_key}}.

Template syntax

Alert templates use the Go template engine and support the following syntax:

Variable reference

{{.fieldName}}          # Reference a field
{{.nested.field}}       # Reference a nested field

Conditional statement

{{if .condition}}
  Displayed if the condition is true
{{else}}
  Displayed if the condition is false
{{end}}

Iteration

{{range .items}}
  - {{.name}}: {{.value}}
{{end}}

Comparison operators

{{if eq .status "ok"}}Normal{{end}}           # Equal to
{{if ne .status "error"}}Not an error{{end}}       # Not equal to
{{if gt .value 100}}Greater than 100{{end}}          # Greater than
{{if lt .value 50}}Less than 50{{end}}            # Less than
{{if ge .value 80}}Greater than or equal to 80{{end}}         # Greater than or equal to
{{if le .value 20}}Less than or equal to 20{{end}}         # Less than or equal to

Logical operators

{{if and .cond1 .cond2}}Both conditions are true{{end}}
{{if or .cond1 .cond2}}At least one condition is true{{end}}
{{if not .condition}}The condition is false{{end}}

Pipelining

{{.value | functionName}}                    # Pass the value to a function
{{.timestamp | humanizeDate}}                # Format a timestamp
{{.ratio | humanizePercentage}}              # Format a percentage

Utility functions

Operator

Syntax

Input type

Output type

Description

humanizeDate

{{.x | humanizeDate}}

float64

string

Converts a millisecond timestamp to a human-readable time.

humanizePercentage

{{.x | humanizePercentage}}

float64

string

Converts a decimal to a percentage.

md5

{{.x | md5}}

string

string

Calculates the MD5 hash of a string.

toJson

{{.x | toJson}}

any

string

Converts a value to a JSON string.

fromJson

{{fromJson .x}}

string

any

Parses a JSON string into a value.

quote

{{.x | quote}}

any

string

Encloses a string in double quotes, escaping any special characters.

printf

{{printf "fmt" .a .b}}

string, ...any

string

Returns a formatted string, similar to Go's fmt.Sprintf.

len

any

int

Returns the length of an array, slice, map, or string.

regexMatch

{{if regexMatch "pattern" .x}}

string, any

bool

Reports whether a string matches a regular expression.

regexFind

{{regexFind "pattern" .x}}

string, any

string

Returns the first substring matching the regular expression.

regexFindAll

{{regexFindAll "pattern" n .x}}

string, int, any

[]string

Returns all substrings matching the regular expression.

regexReplaceAll

{{regexReplaceAll "pattern" "repl" .x}}

string, string, any

string

Replaces all substrings matching the regular expression with a replacement string.

humanizeDate

function: Converts a millisecond timestamp to a human-readable date and time.

input: A millisecond timestamp (float64).

output: A formatted date string, such as 2006-01-02 15:04:05.

example:

{"timestamp": 1634567890000}

template:

Alarm time: {{.timestamp | humanizeDate}}

output:

Alarm time: 2021-10-18 21:04:50

humanizePercentage

Function: Converts a float value to a percentage string with two decimal places.

Input: A float value (float64) ranging from 0 to 1.

Output: A percentage string in the format xx.xx%.

Example:

{"cpu": 0.8567}

Template:

CPU utilization: {{.cpu | humanizePercentage}}

Output:

CPU utilization: 85.67%

md5

Function: Computes the 32-character lowercase hexadecimal md5 hash of a string.

Input: A string.

Output: A 32-character lowercase hexadecimal string.

Example:

{"alertId": "ALERT-001"}

Template:

Alert fingerprint: {{.alertId | md5}}

Output:

Alert fingerprint: 8c1b6fa97c4288cdf2e505475e9c4f8e

toJson

Function: Serializes any value, such as an object, array, or string, to a JSON string.

Input: Any type.

Output: A JSON string.

Example:

{"incident": {"title": "Disk usage exceeded the threshold", "level": "critical"}}

Template:

Alert details: {{.incident | toJson}}

Output:

Alert details: {"level":"critical","title":"Disk usage exceeded the threshold"}

Combined usage:

Data:

{"tags": ["prod", "k8s", "node"]}

Template:

Tag list: {{.tags | toJson}}

Output:

Tag list: ["prod","k8s","node"]
Note
  • When the input is nil, the output is null.

  • If serialization fails, the function returns an error string without interrupting rendering.

  • You can combine it with regexFindAll and other functions that return an array.

fromJson

Function: Deserializes a JSON string into a Go object, enabling object field access and array iteration.

Input: A JSON string.

Output: A parsed array ([]interface{}) or object (map[string]interface{}). If the parsing fails, the original string is returned.

Basic usage:

{{range (fromJson .fieldName)}}...{{end}}
{{(fromJson .fieldName).fieldName}}

Scenario 1 — Iterate over an array of log alerts

When an alert is triggered, the log content in {{ $value }} is a JSON string, such as [{...}, {...}]. You can parse it with fromJson and then iterate over the result:

Data (the value of $value is stored in data as a string):

{
  "logs": "[{\"message\":\"ERROR disk full\",\"hostname\":\"node-01\"},{\"message\":\"WARN high cpu\",\"hostname\":\"node-02\"}]"
}

Template:

Triggered logs:
{{- range (fromJson .logs)}}
- Host: {{.hostname}}  Content: {{.message}}
{{- end}}

Output:

Triggered logs:
- Host: node-01  Content: ERROR disk full
- Host: node-02  Content: WARN high cpu

Scenario 2 — Access object fields

Data:

{"detail": "{\"title\":\"Disk alert\",\"level\":\"critical\"}"}

Template:

Alert title: {{(fromJson .detail).title}}
Alert level: {{(fromJson .detail).level}}

Output:

Alert title: Disk alert
Alert level: critical
Note
  • When the input is a JSON array ([...]), an array for range iteration is returned.

  • Returns a map accessible via .field when the input is a JSON object ({...}).

  • On a parsing failure, the function returns the original string without interrupting template rendering.

  • The fromJson function is the inverse of toJson: toJson serializes, and fromJson deserializes.

regexMatch

Function: Checks if an input matches a regular expression. This is commonly used in {{if}} conditional statements.

Input: A pattern (string) and the value to be matched (any).

Output: A boolean. Returns true if the input matches the pattern, otherwise false. An invalid pattern returns an error.

Basic usage:

{{if regexMatch "regular expression" .fieldName}}...{{end}}

Example:

{"phone": "13812345678"}

Template:

{{if regexMatch "^1[3-9][0-9]{9}$" .phone}}Valid phone number format{{else}}Invalid phone number format{{end}}

Output:

Valid phone number format
Note
  • The regular expression uses the standard Go regexp syntax.

  • regexMatch automatically converts non-string inputs to strings before matching.

  • If the pattern fails to compile, template rendering stops and the function returns an error.

regexFind

Function: Finds the first substring in a value that matches a regular expression.

Input: pattern (string), value (any)

Output: Returns the first matching substring (string), an empty string if no match is found, or an error if the pattern is invalid.

Basic usage:

{{regexFind "regular expression" .fieldName}}

Example:

{"message": "server 192.168.1.100 response timeout"}

Template:

alert IP: {{regexFind "[0-9]{1,3}(\\.[0-9]{1,3}){3}" .message}}

Output:

alert IP: 192.168.1.100
Note
  • Returns only the first match. To find all matches, use regexFindAll.

  • It automatically converts non-string inputs to strings before matching.

regexFindAll

Function: Finds all substrings in the input that match a regular expression and returns a string array.

Input: pattern (string), n (int, the maximum number of matches, where -1 means no limit), source (any)

Output: An array of matched substrings ([]string). Returns an empty array if no matches are found, or an error if the pattern is invalid.

Basic usage:

{{regexFindAll "regular expression" -1 .fieldName}}

Example:

{"log": "ERROR at line 12, WARNING at line 34, ERROR at line 56"}

Template:

Error line numbers: {{regexFindAll "[0-9]+" -1 .log | toJson}}

Output:

Error line numbers: ["12","34","56"]

Scenario: Limit the number of matches

Template:

First two line numbers: {{regexFindAll "[0-9]+" 2 .log | toJson}}

Output:

First two line numbers: ["12","34"]

Scenario: Iterate over all matched results

{"hosts": "web-01,web-02,web-03"}

Template:

{{range regexFindAll "web-[0-9]+" -1 .hosts}}
- host: {{.}}
{{end}}

Output:

- host: web-01
- host: web-02
- host: web-03
Note
  • The function returns all matches if n=-1, and at most n matches if n>0.

  • You can iterate over the results using {{range}} or convert them to a JSON string using toJson.

regexReplaceAll

Function: Replaces all substrings that match a regular expression with a specified string.

Input: pattern (string), replacement (string), input value (any)

Output: Returns the modified string, or an error if the pattern is invalid.

Basic usage:

{{regexReplaceAll "regular expression" "replacement string" .fieldName}}

Example 1: Masking a phone number

{"phone": "13812345678"}

Template:

Contact phone: {{regexReplaceAll "([0-9]{3})[0-9]{4}([0-9]{4})" "${1}****${2}" .phone}}

Output:

Contact phone: 138****5678

Example 2: Redacting sensitive information

{"message": "User token=abc123xyz login failed"}

Template:

{{regexReplaceAll "token=[a-zA-Z0-9]+" "token=***" .message}}

Output:

User token=*** login failed
Note
  • In the replacement string, you can use ${1}${2}, and so on to reference capture groups.

  • The function automatically converts non-string inputs to strings before processing.

Template writing

Examples

Example 1: Format timestamps and percentages

{
  "timestamp": 1634567890000,
  "cpuUsage": 0.8567
}

Template:

Alert time: {{.timestamp | humanizeDate}}
CPU usage: {{.cpuUsage | humanizePercentage}}

Output:

Alert time: 2021-10-18 21:04:50
CPU usage: 85.67%

Example 2: Combine multiple functions

{
  "alertId": "ALERT-001",
  "timestamp": 1634567890000,
  "severity": 0.95,
  "incident": {"service": "order-api", "region": "cn-hangzhou"}
}

Template:

Alert Notification

Alert ID: {{.alertId}}
Alert fingerprint: {{.alertId | md5}}
Alert time: {{.timestamp | humanizeDate}}
Severity: {{.severity | humanizePercentage}}
Alert details: {{.incident | toJson}}

Output:

Alert Notification

Alert ID: ALERT-001
Alert fingerprint: 8c1b6fa97c4288cdf2e505475e9c4f8e
Alert time: 2021-10-18 21:04:50
Severity: 95.00%
Alert details: {"region":"cn-hangzhou","service":"order-api"}

Example 3: Extract key information with regular expressions

{
  "logLine": "2025-08-25 19:00:01 ERROR [order-service] Connection timed out 192.168.1.100:8080"
}

Template:

Source IP: {{regexFind "[0-9]{1,3}(\\.[0-9]{1,3}){3}" .logLine}}
Log level: {{regexFind "ERROR|WARN|INFO" .logLine}}

Output:

Source IP: 192.168.1.100
Log level: ERROR

Example 4: Conditional checks and masking with regular expressions

{
  "phone": "13812345678",
  "email": "user@example.com"
}

Template:

{{if regexMatch "^1[3-9][0-9]{9}$" .phone}}
Contact phone: {{regexReplaceAll "([0-9]{3})[0-9]{4}([0-9]{4})" "${1}****${2}" .phone}}
{{end}}
{{if regexMatch "^[^@]+@[^@]+$" .email}}
Contact email: {{regexReplaceAll "(.{2}).+(@.+)" "${1}***${2}" .email}}
{{end}}

Output:

Contact phone: 138****5678
Contact email: us***@example.com

Best practices

Use descriptive field names

Recommended:
{{.alertName}} {{.triggerTime}} {{.severity}}

Not recommended:
{{.a}} {{.t}} {{.s}}

Handle default values

{{if .description}}
Description: {{.description}}
{{else}}
Description: None
{{end}}

Use utility functions effectively

# Use humanizeDate for timestamp fields
Occurrence time: {{.timestamp | humanizeDate}}

# Use humanizePercentage for ratio fields
Usage: {{.usage | humanizePercentage}}

# Use toJson to serialize object/array fields
Alert details: {{.incident | toJson}}

# Use regexFind to extract substrings from text
Source IP: {{regexFind "[0-9]{1,3}(\\.[0-9]{1,3}){3}" .message}}

Template reference

Alert content template

Alert name: {{.alertName}}
Alert level: {{.level}}
Trigger time: {{.triggerTime | humanizeDate}}
Alert target: {{.target}}
{{if .description}}Alert description: {{.description}}{{end}}
Current value: {{.currentValue}}
Threshold: {{.threshold}}

Alert details template

=== Alert Details ===
• Alert name: {{.alertName}}
• Alert level: {{if eq .level "critical"}}Critical{{else if eq .level "warning"}}Warning{{else}}Info{{end}}
• Trigger time: {{.triggerTime | humanizeDate}}
• Alert target: {{.target}}
{{if .metrics}}
• Metrics:
{{range .metrics}}  - {{.name}}: {{.value}}{{if .unit}}{{.unit}}{{end}}
{{end}}{{end}}
{{if .suggestion}}
• Suggestion: {{.suggestion}}
{{end}}

List rendering template

Alert host list:
{{range .hosts}}
- Host: {{.hostname}}
  IP: {{.ip}}
  CPU: {{.cpu | humanizePercentage}}
  Memory: {{.memory | humanizePercentage}}
{{end}}

Best practices

Parsing the $value log JSON array

When an alert is triggered, if $value is a JSON array string in which each element is a complete log record, this topic describes how to use a template to extract key information and perform data masking.

Original structure of $value

[
  {
    "message":       "2026-03-13 14:10:59.706 - INFO ... Registered instance ...",
    "tk":            "2026-03-13 14:10:59.707",
    "hostname":      "gz-k8s-dev-cpu-node-008",
    "podName":       "logan-registry-0",
    "namespace":     "logan",
    "containerName": "logan-registry",
    "containerImage":"registry.example.com/logancloud/logan-registry:1.12.2",
    "bootName":      "logan-registry",
    "idc":           "gz1-105",
    "delay":         "31.732",
    "ts":            "2026-03-13T06:10:59.707Z",
    "ts_ms":         "1773382259707",
    "topic":         "",
    "fluentbit":     "1",
    "sls":           "1",

    "tag:client_ip":     "120.232.182.210",
    "tag:receive_time":  "1773382293",
    "tag:pack_id":       "B1EDCDCB732A9C3D-402E106",
    "@timestamp":        "2026-03-13T06:11:29.967326280Z",
    "oam/application":   "",

    "raw": "{\"kubernetes\":{\"pod_id\":\"aef10ba8-...\",\"namespace_name\":\"logan\",\"labels\":{...},\"annotations\":{...},...}}"
  }
]
The raw field is itself a JSON string, so you must use fromJson a second time to access the kubernetes information within it.

Parameter access quick reference

Parameter

Access method

Description

message

{{.message}}

The log message body typically ends with \n.

tk

{{.tk}}

Log generation time (string).

hostname

{{.hostname}}

The name of the host.

podName

{{.podName}}

The name of the Pod.

namespace

{{.namespace}}

Kubernetes namespace.

containerName

{{.containerName}}

The name of the container.

containerImage

{{.containerImage}}

Full image path.

idc

{{.idc}}

IDC identifier.

delay

{{.delay}}

Log collection delay (seconds).

tag:client_ip

{{index . "tag:client_ip"}}

If a key contains a colon, you must use index.

@timestamp

{{index . "@timestamp"}}

If a key contains @, you must use index.

oam/application

{{index . "oam/application"}}

If a key contains a slash, you must use index.

rawkubernetes.pod_id

{{(fromJson .raw).kubernetes.pod_id}}

The raw field contains a JSON string that requires nested parsing.

rawkubernetes.namespace_name

{{(fromJson .raw).kubernetes.namespace_name}}

Same as the preceding entry.

raw → key in labels with a period

{{index (fromJson .raw).kubernetes.labels "statefulset.kubernetes.io/pod-name"}}

If a key contains a dot, you must use index.

Data masking approaches

As a best practice, apply data masking at the template level before sending alert notifications.

Masking IP addresses
{{- /* Keep the first two octets and replace the last two with *.* */ -}}
{{regexReplaceAll "([0-9]{1,3}\\.[0-9]{1,3})\\.[0-9]{1,3}\\.[0-9]{1,3}" "${1}.*.*" (index . "tag:client_ip")}}

120.232.182.210120.232.*.*

Masking hostnames
{{- /* Keep the cluster prefix and replace the node number with *** */ -}}
{{regexReplaceAll "(gz-k8s-[a-z]+-[a-z]+-[a-z]+)-[0-9]+" "${1}-***" .hostname}}

gz-k8s-dev-cpu-node-008gz-k8s-dev-cpu-node-***

Masking container registry addresses
{{- /* Keep only the image name and tag, removing the private registry domain */ -}}
{{regexReplaceAll "^[^/]+/[^/]+/(.+)$" "${1}" .containerImage}}

registry.example.com/logancloud/logan-registry:1.12.2logan-registry:1.12.2

Masking the message body
{{- /* Filter credentials in the format token=xxx */ -}}
{{regexReplaceAll "token=[a-zA-Z0-9._-]+" "token=***" .message}}
{{- /* Filter credentials in the format password=xxx */ -}}
{{regexReplaceAll "(?i)password=[^\\s&]+" "password=***" .message}}
Removing trailing newline characters from message
{{regexReplaceAll "\\s+$" "" .message}}

Complete alert template example

The following template demonstrates a complete log alert rendering, including field extraction, special key access, secondary fromJson parsing, and various types of data masking:

{{- range (fromJson $value) -}}
=== Log alert ===
Log time:         {{.tk}}
Collection delay: {{.delay}}s

[Host information]
Host:       {{regexReplaceAll "(gz-k8s-[a-z]+-[a-z]+-[a-z]+)-[0-9]+" "${1}-***" .hostname}}
IDC:        {{.idc}}
Client IP:  {{regexReplaceAll "([0-9]{1,3}\\.[0-9]{1,3})\\.[0-9]{1,3}\\.[0-9]{1,3}" "${1}.*.*" (index . "tag:client_ip")}}

[Container information]
Namespace:  {{.namespace}}
Pod:        {{.podName}}
Container:  {{.containerName}}
Image:      {{regexReplaceAll "^[^/]+/[^/]+/(.+)$" "${1}" .containerImage}}

[K8s information]
Pod ID:     {{(fromJson .raw).kubernetes.pod_id}}
StatefulSet:{{index (fromJson .raw).kubernetes.labels "statefulset.kubernetes.io/pod-name"}}

[Log content]
{{regexReplaceAll "\\s+$" "" .message}}
{{- end}}

Rendered output:

=== Log alert ===
Log time:         2026-03-13 14:10:59.707
Collection delay: 31.732s

[Host information]
Host:       gz-k8s-dev-cpu-node-***
IDC:        gz1-105
Client IP:  120.232.*.*

[Container information]
Namespace:  logan
Pod:        logan-registry-0
Container:  logan-registry
Image:      logan-registry:1.12.2

[K8s information]
Pod ID:     aef10ba8-b870-4eb6-b2f4-74a5393cfcab
StatefulSet:logan-registry-0

[Log content]
2026-03-13 14:10:59.706 - INFO 1 --- [http-nio-8761-exec-26] c.n.e.registry.AbstractInstanceRegistry  : Registered instance XP-MOBILE-STATION-SEARCH-BOOT/...

Displaying logs by level

{{- range (fromJson $value)}}
{{- if regexMatch "ERROR|WARN" .message}}
[{{regexFind "ERROR|WARN" .message}}] {{.tk}} {{.podName}}
{{regexReplaceAll "\\s+$" "" .message}}
{{- end}}
{{- end}}

FAQ

Handling missing fields

Use an if conditional statement to check if a field exists:

{{if .optionalField}}
Field value: {{.optionalField}}
{{else}}
Field value: Not set
{{end}}

Handling incorrect timestamp format

The humanizeDate function accepts a timestamp in milliseconds (a 13-digit number). If you have a timestamp in seconds (a 10-digit number), multiply it by 1,000 during data preparation.

Outputting a nested object

Use toJson to serialize the object into a JSON string:

{{.incident | toJson}}

Handling backslashes in regular expressions

In a Go template string, escape backslashes by doubling them. For example, to match an IP address:

{{regexFind "[0-9]{1,3}(\\.[0-9]{1,3}){3}" .message}}

regexFindAll: Iterating over results

Iterate through the results with range or convert them to a JSON string with toJson:

# Iterate and output
{{range regexFindAll "[0-9]+" -1 .text}}- {{.}}
{{end}}

# Convert to a JSON array string
{{regexFindAll "[0-9]+" -1 .text | toJson}}

Custom function support

The system currently provides 11 utility functions: humanizeDate, humanizePercentage, md5, toJson, quote, printf, len, regexMatch, regexFind, regexFindAll, and regexReplaceAll.

Go template syntax quick reference

Basic syntax

Feature

Syntax

Example

variable reference

{{.var}}

{{.name}}

nested field

{{.a.b}}

{{.incident.title}}

conditional statement

{{if .x}}...{{end}}

{{if .active}}Yes{{end}}

if-else statement

{{if .x}}...{{else}}...{{end}}

{{if .ok}}Success{{else}}Failure{{end}}

multi-branch conditional

{{if .x}}...{{else if .y}}...{{else}}...{{end}}

{{if eq .level "critical"}}Critical{{else if eq .level "warning"}}Warning{{else}}Info{{end}}

loop

{{range .list}}...{{end}}

{{range .items}}{{.name}}{{end}}

loop with index

{{range $i, $v := .list}}...{{end}}

{{range $i, $v := .items}}{{$i}}:{{$v}}{{end}}

pipeline

{{.x | func}}

{{.timestamp | humanizeDate}}

chained pipeline

{{.x | func1 | func2}}

{{.incident | toJson | quote}}

variable assignment

{{$var := .x}}

{{$name := .alertName}}

array element access

{{index .arr n}}

{{index .items 0}}

map element access

{{index .map "key"}}

{{index .labels "env"}}

Length

{{len .x}}

{{len .items}}

Comparison operations

Feature

Syntax

Example

equal to

{{if eq .a .b}}

{{if eq .status "ok"}}Normal{{end}}

not equal to

{{if ne .a .b}}

{{if ne .code 0}}Error{{end}}

greater than

{{if gt .a .b}}

{{if gt .value 100}}Exceeds threshold{{end}}

less than

{{if lt .a .b}}

{{if lt .score 60}}Fail{{end}}

greater than or equal to

{{if ge .a .b}}

{{if ge .value 80}}High{{end}}

less than or equal to

{{if le .a .b}}

{{if le .value 20}}Low{{end}}

Logical operations

Feature

Syntax

Example

and

{{if and .a .b}}

{{if and .active .healthy}}Running normally{{end}}

or

{{if or .a .b}}

{{if or .error .timeout}}Abnormal{{end}}

not

{{if not .x}}

{{if not .disabled}}Enabled{{end}}