Code Templates
Code templates are commonly used in Advice. They provide a simple way to create new AST nodes that will be injected into the instrumented file, allowing references to the matched node.
Generalities
Template Syntax
Orchestrion code templates are rendered using the Go standard library
text/
module. Refer to the module’s documentation to learn
about the general syntax of these template values.
In addition to the template text, Orchestrion code templates allow for an
additional imports
configuration objects as well as an optional lang
value.
Imports
The imports
map binds Go language identifiers to package import paths. All
entities (functions, types, etc…) that are used in the template text and
which are not local to the instrumented package must be accessed through the
identifier from the imports
map, even if that package is already imported by
the instrumented file.
See the following examples:
template: fmt.Println({{ . }})
imports:
# The template uses `fmt` so we bind it to the identifier `fmt` below.
fmt: fmt
template: |-
tracer.Start()
defer tracer.Stop()
imports:
tracer: gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer
Language level
The lang
setting allows a template to declare a minimum require Go language
level. This is useful when aspects are woven into modules with a go.mod
file
declaring an older go
directive; as the user of newer syntax by the aspects
will then break the compilation step.
Setting lang: go1.18
for example allows the use of Go generics even when
injecting packages that allow for really old Go toolchains to be used
(orchestrion is more strict, as it only supports recent toolchains).
If unspecified, no particular language requirement is imposed, so the language
level specified by the currently compiled module’s go.mod
file will be used.
lang: go1.18 # Uses go generics
template: |-
func Clone[E any, S ~[]E](slice S) S {
res := make(S, len(slice))
copy(res, slice)
return res
}
Template context
This section documents what functions are available for use in template text, as
well as what the .
context value represents, and which methods are
available for use on it. These are essential for authoring Orchestrion code
templates.
The Version
function
The simple Version
function returns the version of Orchestrion being used, as
represented by the version tag name.
- Given:
fmt.Println("Orchestrion version is: {{ Version }}")
- The output would look like:
fmt.Println("Orchestrion version is: v0.7.3")
The value of .
In orchestrion code templates, .
is a value that represents information about
the node matched by the join point that led to evaluation of the current
advice.
Simply using {{ . }}
results in moving the current AST node into the
template’s output. The moved AST node retains its original source position
information, which is essential to avoid unnecessarily affecting the
application’s stack trace information.
A given AST node may be present only exactly once in the resulting AST, so template authors must be careful to avoid producing the same node in multiple places.
The .AST
method can be used to copy nodes (instead of moving them), making
them safe to produce in multiple locations in the output.
The .AST
method
The .AST
method returns a view of the node’s AST. This view allows refering
to all attributes of the original AST nodes, producing a similar view object for
each child node.
When view objects are rendered by the template, they move the view’s
underlying node. Use the view’s .Copy
method to instead copy the node into
the target AST. The copied AST nodes are detached from the original node’s
source location, making them appear as synthetic in the produced output, meaning
they would be reported as belonging to generated code in the application’s stack
trace.
The complete type hierarchy for these views corresponds to the
dst.Node
implementations, which provide the
underlying AST model. Node properties can be accessed using their usual name
from github.com/
.
The .DirectiveArgs
method
A directive is a specially formatted single-line comment, without white space
between the //
and the directive name, similar to the //go:linkname
directive.
The .DirectiveArgs
method can be used to look up the AST in order to locate a
given directive, and to extract key-value pair arguments from it. It requires
a directive name argument, and returns the list of values that have .Key
and
.Value
fields, respectively allowing access to the argument’s key and value.
Here is an example of how this can be used, from the instrumentation of
//dd:span
directives:
{{- $ctx := .Function.ArgumentOfType "context.Context" -}}
{{- $name := .Function.Name -}}
{{$ctx}} = instrument.Report({{$ctx}}, event.EventStart{{with $name}}, "function-name", {{printf "%q" .}}{{end}}
{{- range .DirectiveArgs "dd:span" -}}
, {{printf "%q" .Key}}, {{printf "%q" .Value}}
{{- end -}})
defer instrument.Report({{$ctx}}, event.EventEnd{{with $name}}, "function-name", {{printf "%q" .}}{{end}}
{{- range .DirectiveArgs "dd:span" -}}
, {{printf "%q" .Key}}, {{printf "%q" .Value}}
{{- end -}})
The .Function
method
The .Function
method walks up the AST to find the closest enclosing function.
This can be used within a template to access the enclosing function’s name,
parameters or return values.
It accepts no argument and returns an object that ptovides access to the function’s signature details:
.Function.Name
returns the function’s name, or a blank string if the function is a function literal expression.Function.Receiver
returns the name of the receiver value for this function. Returns an error if the surrounding function is not a method..Function.Argument n
returns the name of then
th argument (0
-based) of the function; and implicitly assigns it a name if the argument was anonymous or named_
. Returns an error if the surrounding function does not have enough arguments..Function.ArgumentOfType type
returns the name of the first argument that has the specified type; or a blank string if no such argument exists. The type is provided as a fully qualified type name (e.g,*net/http.ResponseWriter
).Function.Result n
returns the name of then
th return value (0
-based) of the function; and implicitly assigns it a name if the return value was anonymous or named_
. Returns an error if the surrounding function does not have enough return values..Function.ResultOfType type
returns the name of the first result value that has the specified type; or a blank string if no such result value exists. The type is provided as a fully qualified type name (e.g,*net/http.ResponseWriter
).