summaryrefslogtreecommitdiffstats
path: root/third_party/python/glean_parser/glean_parser/templates/go_server.jinja2
blob: 0a26831b0fcaebd1dc55e9786f9464b3df9527ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
{# The final Go code is autogenerated, but this template is not. Please file bugs! #}
package glean

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// AUTOGENERATED BY glean_parser v{{ parser_version }}. DO NOT EDIT.

// required imports
import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/google/uuid"
)

// log type string used to identify logs to process in the Moz Data Pipeline
var gleanEventMozlogType string = "glean-server-event"

type GleanEventsLogger struct {
	AppID             string // Application Id to identify application per Glean standards
	AppDisplayVersion string // Version of application emitting the event
	AppChannel        string // Channel to differentiate logs from prod/beta/staging/devel
}

// exported type for public method parameters
type RequestInfo struct {
	UserAgent string
	IpAddress string
}

// default empty values will be omitted in json from ping struct definition
var defaultRequestInfo = RequestInfo{
	UserAgent: "",
	IpAddress: "",
}

// structs to construct the glean ping
type clientInfo struct {
	TelemetrySDKBuild string `json:"telemetry_sdk_build"`
	FirstRunDate      string `json:"first_run_date"`
	OS                string `json:"os"`
	OSVersion         string `json:"os_version"`
	Architecture      string `json:"architecture"`
	AppBuild          string `json:"app_build"`
	AppDisplayVersion string `json:"app_display_version"`
	AppChannel        string `json:"app_channel"`
}

type pingInfo struct {
	Seq       int    `json:"seq"`
	StartTime string `json:"start_time"`
	EndTime   string `json:"end_time"`
}

type ping struct {
	DocumentNamespace string `json:"document_namespace"`
	DocumentType      string `json:"document_type"`
	DocumentVersion   string `json:"document_version"`
	DocumentID        string `json:"document_id"`
	UserAgent         string `json:"user_agent,omitempty"`
	IpAddress         string `json:"ip_address,omitempty"`
	Payload           string `json:"payload"`
}

type metrics map[string]map[string]interface{}

type pingPayload struct {
	ClientInfo clientInfo   `json:"client_info"`
	PingInfo   pingInfo     `json:"ping_info"`
	Metrics    metrics      `json:"metrics"`
	Events     []gleanEvent `json:"events"`
}

type gleanEvent struct {
	Category  string            `json:"category"`
	Name      string            `json:"name"`
	Timestamp int64             `json:"timestamp"`
	Extra     map[string]string `json:"extra"`
}

type logEnvelope struct {
	Timestamp string
	Logger    string
	Type      string
	Fields    ping
}

func (g GleanEventsLogger) createClientInfo() clientInfo {
	// Fields with default values are required in the Glean schema, but not used in server context
	return clientInfo{
		TelemetrySDKBuild: "glean_parser v{{ parser_version }}",
		FirstRunDate:      "Unknown",
		OS:                "Unknown",
		OSVersion:         "Unknown",
		Architecture:      "Unknown",
		AppBuild:          "Unknown",
		AppDisplayVersion: g.AppDisplayVersion,
		AppChannel:        g.AppChannel,
	}
}

func createPingInfo() pingInfo {
	{# times are ISO-8601 strings, e.g. "2023-12-19T22:09:17.440Z" #}
	var now = time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
	return pingInfo{
		Seq:       0,
		StartTime: now,
		EndTime:   now,
	}
}

func (g GleanEventsLogger) createPing(documentType string, config RequestInfo, payload pingPayload) ping {
	var payloadJson, payloadErr = json.Marshal(payload)
	if payloadErr != nil {
		panic("Unable to marshal payload to json")
	}
	var documentId = uuid.New()
	return ping{
		DocumentNamespace: g.AppID,
		DocumentType:      documentType,
		DocumentVersion:   "1",
		DocumentID:        documentId.String(),
		UserAgent:         config.UserAgent,
		IpAddress:         config.IpAddress,
		Payload:           string(payloadJson),
	}
}

// method called by each event method.
// construct the ping, wrap it in the envelope, and print to stdout
func (g GleanEventsLogger) record(
	documentType string,
	requestInfo RequestInfo,
	metrics metrics,
	events []gleanEvent,
) {
	var telemetryPayload = pingPayload{
		ClientInfo: g.createClientInfo(),
		PingInfo:   createPingInfo(),
		Metrics:    metrics,
		Events:     events,
	}

	var ping = g.createPing(documentType, requestInfo, telemetryPayload)

	var envelope = logEnvelope{
		Timestamp: strconv.FormatInt(time.Now().UnixNano(), 10),
		Logger:    "glean",
		Type:      gleanEventMozlogType,
		Fields:    ping,
	}
	var envelopeJson, envelopeErr = json.Marshal(envelope)
	if envelopeErr != nil {
		panic("Unable to marshal log envelope to json")
	}
	fmt.Println(string(envelopeJson))
}

{% for event in events_ping["event"] %}
type {{ event|event_type_name }} struct {
  {% for metric_type, metrics in events_ping.items() %}
  {% if metric_type != 'event' %}
  {% for metric in metrics %}
  {{ metric|metric_argument_name }} {{ metric.type|go_metric_type }} // {{ metric.description|clean_string }}
  {% endfor %}
  {% endif %}
  {% endfor %}
  {% for extra, metadata in event.extra_keys.items() %}
  {{ extra|event_extra_name }} {{ metadata.type|go_metric_type }} // {{ metadata.description|clean_string }}
  {% endfor %}
}

// Record and submit an {{ event|event_type_name }} event.
// {{ event.description|clean_string }}
func (g GleanEventsLogger) Record{{ event|event_type_name }}(
	requestInfo RequestInfo,
	params {{ event|event_type_name }},
) {
	var metrics = metrics{
    {% for metric_type, metrics in events_ping.items() %}
		{% if metric_type != 'event' %}
		"{{ metric_type }}": {
		{% for metric in metrics %}
			"{{ metric|metric_name }}": params.{{ metric|metric_argument_name }},
		{% endfor %}
		},
		{% endif %}
    {% endfor %}
	}
	var extraKeys = map[string]string{
    {% for extra, metadata in event.extra_keys.items() %}
		{# convert all extra fields to string for submission #}
		{% if metadata.type == 'boolean' %}
		"{{ extra }}": fmt.Sprintf("%t", params.{{ extra|event_extra_name }}),
		{% elif metadata.type == 'quantity' %}
		"{{ extra }}": fmt.Sprintf("%d", params.{{ extra|event_extra_name }}),
		{% else %}
		"{{ extra }}": params.{{ extra|event_extra_name }},
		{% endif %}
    {% endfor %}
	}
	var events = []gleanEvent{
		gleanEvent{
			Category:  "{{ event.category }}",
			Name:      "{{ event.name }}",
			Timestamp: time.Now().UnixMilli(),
			Extra:     extraKeys,
		},
	}
	g.record("events", requestInfo, metrics, events)
}

// Record and submit an {{ event|event_type_name }} event omitting user request info
// {{ event.description|clean_string }}
func (g GleanEventsLogger) Record{{ event|event_type_name }}WithoutUserInfo(
  params {{ event|event_type_name }},
) {
	g.Record{{ event|event_type_name }}(defaultRequestInfo, params)
}

{% endfor %}