1 package machineoutput
2
3 import (
4 "bufio"
5 "errors"
6 "fmt"
7 "io"
8 "time"
9
10 "github.com/redhat-developer/odo/pkg/log"
11
12 "k8s.io/klog"
13 )
14
15
16 func FormatTime(time time.Time) string {
17 result := fmt.Sprintf("%d.%06d", time.Unix(), time.Nanosecond()/1000)
18 return result
19
20 }
21
22
23 func TimestampNow() string {
24 return FormatTime(time.Now())
25 }
26
27
28 func NewMachineEventLoggingClient() MachineEventLoggingClient {
29 if log.IsJSON() {
30 return NewConsoleMachineEventLoggingClient()
31 }
32
33 return NewNoOpMachineEventLoggingClient()
34 }
35
36
37
38 func NewNoOpMachineEventLoggingClient() *NoOpMachineEventLoggingClient {
39 return &NoOpMachineEventLoggingClient{}
40 }
41
42 var _ MachineEventLoggingClient = &NoOpMachineEventLoggingClient{}
43
44
45 func (c *NoOpMachineEventLoggingClient) DevFileCommandExecutionBegin(commandID string, componentName string, commandLine string, groupKind string, timestamp string) {
46 }
47
48
49 func (c *NoOpMachineEventLoggingClient) DevFileCommandExecutionComplete(commandID string, componentName string, commandLine string, groupKind string, timestamp string, errorVal error) {
50 }
51
52
53 func (c *NoOpMachineEventLoggingClient) CreateContainerOutputWriter() (*io.PipeWriter, chan interface{}, *io.PipeWriter, chan interface{}) {
54
55 channels := []chan interface{}{make(chan interface{}), make(chan interface{})}
56
57
58 for _, channelPtr := range channels {
59 channelVal := channelPtr
60
61 go func(channel chan interface{}) {
62 for {
63 channel <- nil
64 }
65 }(channelVal)
66 }
67
68 return nil, channels[0], nil, channels[1]
69 }
70
71
72 func (c *NoOpMachineEventLoggingClient) ReportError(errorVal error, timestamp string) {}
73
74
75 func (c *NoOpMachineEventLoggingClient) ContainerStatus(statuses []ContainerStatusEntry, timestamp string) {
76 }
77
78
79 func (c *NoOpMachineEventLoggingClient) URLReachable(name string, url string, port int, secure bool, kind string, reachable bool, timestamp string) {
80
81 }
82
83
84 func (c *NoOpMachineEventLoggingClient) KubernetesPodStatus(pods []KubernetesPodStatusEntry, timestamp string) {
85
86 }
87
88
89
90 func NewConsoleMachineEventLoggingClient() *ConsoleMachineEventLoggingClient {
91 return &ConsoleMachineEventLoggingClient{}
92 }
93
94 var _ MachineEventLoggingClient = &ConsoleMachineEventLoggingClient{}
95
96
97 func (c *ConsoleMachineEventLoggingClient) DevFileCommandExecutionBegin(commandID string, componentName string, commandLine string, groupKind string, timestamp string) {
98
99 json := MachineEventWrapper{
100 DevFileCommandExecutionBegin: &DevFileCommandExecutionBegin{
101 CommandID: commandID,
102 ComponentName: componentName,
103 CommandLine: commandLine,
104 GroupKind: groupKind,
105 AbstractLogEvent: AbstractLogEvent{Timestamp: timestamp},
106 },
107 }
108
109 c.outputJSON(json)
110 }
111
112
113 func (c *ConsoleMachineEventLoggingClient) DevFileCommandExecutionComplete(commandID string, componentName string, commandLine string, groupKind string, timestamp string, errorVal error) {
114
115 errorStr := ""
116
117 if errorVal != nil {
118 errorStr = errorVal.Error()
119 }
120
121 json := MachineEventWrapper{
122 DevFileCommandExecutionComplete: &DevFileCommandExecutionComplete{
123 DevFileCommandExecutionBegin: DevFileCommandExecutionBegin{
124 CommandID: commandID,
125 ComponentName: componentName,
126 CommandLine: commandLine,
127 GroupKind: groupKind,
128 AbstractLogEvent: AbstractLogEvent{Timestamp: timestamp},
129 },
130 Error: errorStr,
131 },
132 }
133
134 c.outputJSON(json)
135 }
136
137
138
139
140
141
142
143 func (c *ConsoleMachineEventLoggingClient) CreateContainerOutputWriter() (*io.PipeWriter, chan interface{}, *io.PipeWriter, chan interface{}) {
144
145 stdoutWriter, stdoutChannel := createWriterAndChannel(false)
146 stderrWriter, stderrChannel := createWriterAndChannel(true)
147
148 return stdoutWriter, stdoutChannel, stderrWriter, stderrChannel
149
150 }
151
152
153 func (c *ConsoleMachineEventLoggingClient) ReportError(errorVal error, timestamp string) {
154 json := MachineEventWrapper{
155 ReportError: &ReportError{
156 Error: errorVal.Error(),
157 AbstractLogEvent: AbstractLogEvent{Timestamp: timestamp},
158 },
159 }
160
161 c.outputJSON(json)
162 }
163
164
165 func (c *ConsoleMachineEventLoggingClient) ContainerStatus(statuses []ContainerStatusEntry, timestamp string) {
166 json := MachineEventWrapper{
167 ContainerStatus: &ContainerStatus{
168 Status: statuses,
169 AbstractLogEvent: AbstractLogEvent{
170 Timestamp: timestamp,
171 },
172 },
173 }
174 c.outputJSON(json)
175 }
176
177
178 func (c *ConsoleMachineEventLoggingClient) URLReachable(name string, url string, port int, secure bool, kind string, reachable bool, timestamp string) {
179 json := MachineEventWrapper{
180 URLReachable: &URLReachable{
181 Name: name,
182 URL: url,
183 Port: port,
184 Secure: secure,
185 Kind: kind,
186 Reachable: reachable,
187 AbstractLogEvent: AbstractLogEvent{Timestamp: timestamp},
188 },
189 }
190 c.outputJSON(json)
191 }
192
193
194 func (c *ConsoleMachineEventLoggingClient) KubernetesPodStatus(pods []KubernetesPodStatusEntry, timestamp string) {
195 json := MachineEventWrapper{
196 KubernetesPodStatus: &KubernetesPodStatus{
197 Pods: pods,
198 AbstractLogEvent: AbstractLogEvent{
199 Timestamp: timestamp,
200 },
201 },
202 }
203 c.outputJSON(json)
204 }
205
206 func (c *ConsoleMachineEventLoggingClient) outputJSON(machineOutput MachineEventWrapper) {
207
208 if c.logFunc != nil {
209 c.logFunc(machineOutput)
210 return
211 }
212
213 OutputSuccessUnindented(machineOutput)
214 }
215
216
217
218 func (w MachineEventWrapper) GetEntry() (MachineEventLogEntry, error) {
219 if w.DevFileCommandExecutionBegin != nil {
220 return w.DevFileCommandExecutionBegin, nil
221 }
222
223 if w.DevFileCommandExecutionComplete != nil {
224 return w.DevFileCommandExecutionComplete, nil
225 }
226
227 if w.LogText != nil {
228 return w.LogText, nil
229 }
230
231 if w.ReportError != nil {
232 return w.ReportError, nil
233 }
234
235 if w.KubernetesPodStatus != nil {
236 return w.KubernetesPodStatus, nil
237 }
238
239 if w.ContainerStatus != nil {
240 return w.ContainerStatus, nil
241 }
242
243 if w.URLReachable != nil {
244 return w.URLReachable, nil
245 }
246
247 return nil, errors.New("unexpected machine event log entry")
248 }
249
250
251 func (c AbstractLogEvent) GetTimestamp() string { return c.Timestamp }
252
253
254 func (c DevFileCommandExecutionBegin) GetType() MachineEventLogEntryType {
255 return TypeDevFileCommandExecutionBegin
256 }
257
258
259 func (c DevFileCommandExecutionComplete) GetType() MachineEventLogEntryType {
260 return TypeDevFileCommandExecutionComplete
261 }
262
263
264 func (c LogText) GetType() MachineEventLogEntryType { return TypeLogText }
265
266
267 func (c ReportError) GetType() MachineEventLogEntryType { return TypeReportError }
268
269
270 func (c ContainerStatus) GetType() MachineEventLogEntryType { return TypeContainerStatus }
271
272
273 func (c URLReachable) GetType() MachineEventLogEntryType { return TypeURLReachable }
274
275
276 func (c KubernetesPodStatus) GetType() MachineEventLogEntryType { return TypeKubernetesPodStatus }
277
278
279 type MachineEventLogEntryType int
280
281 const (
282
283 TypeDevFileCommandExecutionBegin MachineEventLogEntryType = 0
284
285 TypeDevFileCommandExecutionComplete MachineEventLogEntryType = 1
286
287 TypeLogText MachineEventLogEntryType = 2
288
289 TypeReportError MachineEventLogEntryType = 3
290
291 TypeContainerStatus MachineEventLogEntryType = 5
292
293 TypeURLReachable MachineEventLogEntryType = 6
294
295 TypeKubernetesPodStatus MachineEventLogEntryType = 7
296 )
297
298
299 func createWriterAndChannel(stderr bool) (*io.PipeWriter, chan interface{}) {
300 reader, writer := io.Pipe()
301
302 closeChannel := make(chan interface{})
303
304 stream := "stdout"
305 if stderr {
306 stream = "stderr"
307 }
308
309 go func() {
310
311 bufReader := bufio.NewReader(reader)
312 for {
313 line, _, err := bufReader.ReadLine()
314 if err != nil {
315 if err != io.EOF {
316 klog.V(4).Infof("Unexpected error on reading container output reader: %v", err)
317 }
318 break
319 }
320
321
322 json := MachineEventWrapper{
323 LogText: &LogText{
324 AbstractLogEvent: AbstractLogEvent{Timestamp: TimestampNow()},
325 Text: string(line),
326 Stream: stream,
327 },
328 }
329 OutputSuccessUnindented(json)
330 }
331
332
333
334 closeChannel <- nil
335 }()
336
337 return writer, closeChannel
338 }
339
View as plain text