-
Notifications
You must be signed in to change notification settings - Fork 186
Expand file tree
/
Copy pathJsonColumnizer.cs
More file actions
218 lines (172 loc) · 6.39 KB
/
JsonColumnizer.cs
File metadata and controls
218 lines (172 loc) · 6.39 KB
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
using LogExpert;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonColumnizer;
/// <summary>
/// This Columnizer can parse JSON files.
/// </summary>
public class JsonColumnizer : ILogLineColumnizer, IInitColumnizer, IColumnizerPriority
{
#region Properties
public HashSet<string> ColumnSet { get; set; } = [];
protected IList<JsonColumn> ColumnList { get; } = new List<JsonColumn>([InitialColumn]);
protected static JsonColumn InitialColumn { get; } = new JsonColumn("Text");
#endregion
#region Public methods
public virtual void Selected (ILogLineColumnizerCallback callback)
{
ColumnList.Clear();
ColumnSet.Clear();
var line = callback.GetLogLine(0);
if (line != null)
{
var json = ParseJson(line);
if (json != null)
{
var fieldCount = json.Properties().Count();
for (var i = 0; i < fieldCount; ++i)
{
var columeName = json.Properties().ToArray()[i].Name;
if (ColumnSet.Add(columeName))
{
ColumnList.Add(new JsonColumn(columeName));
}
}
}
else
{
_ = ColumnSet.Add("Text");
ColumnList.Add(InitialColumn);
}
}
if (ColumnList.Count == 0)
{
_ = ColumnSet.Add("Text");
ColumnList.Add(InitialColumn);
}
}
public virtual void DeSelected (ILogLineColumnizerCallback callback)
{
// nothing to do
}
public virtual string GetName ()
{
return "JSON Columnizer";
}
public virtual string GetDescription ()
{
return "Splits JSON files into columns.\r\n\r\nCredits:\r\nThis Columnizer uses the Newtonsoft json package.\r\n\r\nFirst line must be valid or else only one column will be displayed and the other values dropped!";
}
public virtual int GetColumnCount ()
{
return ColumnList.Count;
}
public virtual string[] GetColumnNames ()
{
var names = new string[GetColumnCount()];
var i = 0;
foreach (var column in ColumnList)
{
names[i++] = column.Name;
}
return names;
}
public virtual IColumnizedLogLine SplitLine (ILogLineColumnizerCallback callback, ILogLine line)
{
var json = ParseJson(line);
if (json != null)
{
return SplitJsonLine(line, json);
}
var cLogLine = new ColumnizedLogLine { LogLine = line };
var columns = Column.CreateColumns(ColumnList.Count, cLogLine);
columns.Last().FullValue = line.FullLine;
cLogLine.ColumnValues = columns.Select(a => (IColumn)a).ToArray();
return cLogLine;
}
public virtual bool IsTimeshiftImplemented ()
{
return false;
}
public virtual void SetTimeOffset (int msecOffset)
{
throw new NotImplementedException();
}
public virtual int GetTimeOffset ()
{
throw new NotImplementedException();
}
public virtual DateTime GetTimestamp (ILogLineColumnizerCallback callback, ILogLine line)
{
throw new NotImplementedException();
}
public virtual void PushValue (ILogLineColumnizerCallback callback, int column, string value, string oldValue)
{
throw new NotImplementedException();
}
public virtual Priority GetPriority (string fileName, IEnumerable<ILogLine> samples)
{
var result = Priority.NotSupport;
if (fileName.EndsWith("json", StringComparison.OrdinalIgnoreCase))
{
result = Priority.WellSupport;
}
return result;
}
#endregion
#region Private Methods
protected static JObject ParseJson (ILogLine line)
{
return JsonConvert.DeserializeObject<JObject>(line.FullLine, new JsonSerializerSettings()
{
//We ignore the error and handle the null value
Error = (sender, args) => args.ErrorContext.Handled = true
});
}
public class ColumnWithName : Column
{
public string ColumnName { get; set; }
}
//
// Following two log lines should be loaded and displayed in correct grid.
// {"time":"2019-02-13T02:55:35.5186240Z","message":"Hosting starting"}
// {"time":"2019-02-13T02:55:35.5186240Z","level":"warning", "message":"invalid host."}
//
protected virtual IColumnizedLogLine SplitJsonLine (ILogLine line, JObject json)
{
var cLogLine = new ColumnizedLogLine { LogLine = line };
var columns = json.Properties().Select(property => new ColumnWithName { FullValue = property.Value.ToString(), ColumnName = property.Name.ToString(), Parent = cLogLine }).ToList();
foreach (var jsonColumn in columns)
{
// When find new column in a log line, add a new column in the end of the list.
if (!ColumnSet.Contains(jsonColumn.ColumnName))
{
if (ColumnList.Count == 1 && !ColumnSet.Contains(ColumnList[0].Name))
{
ColumnList.Clear();
}
_ = ColumnSet.Add(jsonColumn.ColumnName);
ColumnList.Add(new JsonColumn(jsonColumn.ColumnName));
}
}
//
// Always rearrange the order of all json fields within a line to follow the sequence of columnNameList.
// This will make sure the log line displayed correct even the order of json fields changed.
//
List<IColumn> returnColumns = [];
foreach (var column in ColumnList)
{
var existingColumn = columns.Find(x => x.ColumnName == column.Name);
if (existingColumn != null)
{
returnColumns.Add(new Column() { FullValue = existingColumn.FullValue, Parent = cLogLine });
continue;
}
// Fields that is missing in current line should be shown as empty.
returnColumns.Add(new Column() { FullValue = "", Parent = cLogLine });
}
cLogLine.ColumnValues = [.. returnColumns];
return cLogLine;
}
#endregion
}