Skip to content

Commit f7e59c8

Browse files
d-shapiroa-hansen
authored andcommitted
add Logging Service to sys node
1 parent 24efa46 commit f7e59c8

File tree

4 files changed

+331
-0
lines changed

4 files changed

+331
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.acuity.iot.dsa.dslink.sys.logging;
2+
3+
import java.util.logging.LogManager;
4+
import java.util.logging.Logger;
5+
import org.iot.dsa.node.DSIObject;
6+
import org.iot.dsa.node.DSInfo;
7+
import org.iot.dsa.node.DSMap;
8+
import org.iot.dsa.node.DSString;
9+
import org.iot.dsa.node.action.ActionInvocation;
10+
import org.iot.dsa.node.action.ActionResult;
11+
import org.iot.dsa.node.action.DSAbstractAction;
12+
13+
public class LoggerNode extends StreamableLogNode {
14+
15+
private DSInfo levelInfo = getInfo("Log Level");
16+
17+
public LoggerNode() {
18+
}
19+
20+
public Logger getLoggerObj() {
21+
return LogManager.getLogManager().getLogger(getName());
22+
}
23+
24+
public DSInfo getLevelInfo() {
25+
return levelInfo;
26+
}
27+
28+
@Override
29+
protected void declareDefaults() {
30+
super.declareDefaults();
31+
// declareDefault("Edit", getEditAction());
32+
declareDefault("Refresh", getRefreshAction());
33+
declareDefault("Get Streaming Log", getStreamLogAction());
34+
declareDefault("Remove", getRemoveAction());
35+
declareDefault("Log Level", DSString.valueOf("Unknown"));
36+
}
37+
38+
@Override
39+
protected void onStable() {
40+
super.onStable();
41+
}
42+
43+
private DSIObject getRemoveAction() {
44+
DSAbstractAction act = new DSAbstractAction() {
45+
46+
@Override
47+
public void prepareParameter(DSInfo info, DSMap parameter) {
48+
}
49+
50+
@Override
51+
public ActionResult invoke(DSInfo info, ActionInvocation invocation) {
52+
((LoggerNode) info.getParent()).remove();
53+
return null;
54+
}
55+
};
56+
return act;
57+
}
58+
59+
private void remove() {
60+
getParent().remove(getInfo());
61+
}
62+
63+
private DSAbstractAction getRefreshAction() {
64+
DSAbstractAction act = new DSAbstractAction() {
65+
@Override
66+
public void prepareParameter(DSInfo info, DSMap parameter) {
67+
}
68+
69+
@Override
70+
public ActionResult invoke(DSInfo info, ActionInvocation invocation) {
71+
((LoggerNode) info.getParent()).updateLevel(-1);
72+
return null;
73+
}
74+
};
75+
return act;
76+
}
77+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.acuity.iot.dsa.dslink.sys.logging;
2+
3+
import java.util.Collections;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
import java.util.logging.Handler;
7+
import java.util.logging.Level;
8+
import java.util.logging.LogRecord;
9+
import java.util.logging.Logger;
10+
import org.iot.dsa.DSRuntime;
11+
import org.iot.dsa.logging.DSLogHandler;
12+
import org.iot.dsa.node.DSFlexEnum;
13+
import org.iot.dsa.node.DSIValue;
14+
import org.iot.dsa.node.DSInfo;
15+
import org.iot.dsa.node.DSList;
16+
import org.iot.dsa.node.DSMap;
17+
import org.iot.dsa.node.DSMetadata;
18+
import org.iot.dsa.node.DSNode;
19+
import org.iot.dsa.node.DSValueType;
20+
import org.iot.dsa.node.action.ActionInvocation;
21+
import org.iot.dsa.node.action.ActionResult;
22+
import org.iot.dsa.node.action.ActionSpec;
23+
import org.iot.dsa.node.action.ActionTable;
24+
import org.iot.dsa.node.action.DSAbstractAction;
25+
import org.iot.dsa.node.action.ActionSpec.ResultType;
26+
27+
public abstract class StreamableLogNode extends DSNode {
28+
29+
private static DSList levelRange = new DSList().add(Level.ALL.toString())
30+
.add(Level.CONFIG.toString()).add(Level.FINE.toString()).add(Level.FINER.toString())
31+
.add(Level.FINEST.toString()).add(Level.INFO.toString()).add(Level.OFF.toString())
32+
.add(Level.SEVERE.toString()).add(Level.WARNING.toString());
33+
34+
public abstract Logger getLoggerObj();
35+
36+
public abstract DSInfo getLevelInfo();
37+
38+
@Override
39+
protected void onStable() {
40+
super.onStable();
41+
updateLevel(500);
42+
}
43+
44+
protected DSAbstractAction getStreamLogAction() {
45+
DSAbstractAction act = new DSAbstractAction() {
46+
47+
@Override
48+
public void prepareParameter(DSInfo info, DSMap parameter) {
49+
}
50+
51+
@Override
52+
public ActionResult invoke(DSInfo info, ActionInvocation invocation) {
53+
return ((StreamableLogNode) info.getParent()).startLogStream(info, invocation);
54+
}
55+
};
56+
act.addParameter("Filter", DSValueType.STRING, "Optional Regex filter");
57+
act.setResultType(ResultType.STREAM_TABLE);
58+
act.addValueResult("Log", DSValueType.STRING).setEditor("textarea");
59+
return act;
60+
}
61+
62+
private ActionTable startLogStream(final DSInfo actionInfo, final ActionInvocation invocation) {
63+
final Logger loggerObj = getLoggerObj();
64+
final String filter = invocation.getParameters().getString("Filter");
65+
final Handler handler = new DSLogHandler() {
66+
67+
@Override
68+
protected void write(LogRecord record) {
69+
String line = toString(this, record);
70+
if (filter == null || line.matches(filter)) {
71+
invocation.send(new DSList().add(line));
72+
}
73+
}
74+
75+
// @Override
76+
// public void close() {
77+
// super.close();
78+
// invocation.close();
79+
// }
80+
81+
};
82+
loggerObj.addHandler(handler);
83+
DSMap col = new DSMetadata().setName("Record").setType(DSValueType.STRING).getMap();
84+
final List<DSMap> columns = Collections.singletonList(col);
85+
86+
return new ActionTable() {
87+
88+
@Override
89+
public void onClose() {
90+
handler.close();
91+
loggerObj.removeHandler(handler);
92+
}
93+
94+
@Override
95+
public ActionSpec getAction() {
96+
return actionInfo.getAction();
97+
}
98+
99+
@Override
100+
public Iterator<DSList> getRows() {
101+
List<DSList> empty = Collections.emptyList();
102+
return empty.iterator();
103+
}
104+
105+
@Override
106+
public Iterator<DSMap> getColumns() {
107+
return columns.iterator();
108+
}
109+
};
110+
}
111+
112+
@Override
113+
public void onSet(DSInfo info, DSIValue value) {
114+
super.onSet(info, value);
115+
if (info == getLevelInfo()) {
116+
updateLevel(-1);
117+
}
118+
}
119+
120+
protected void updateLevel(long retryDelay) {
121+
if ("Unknown".equals(getLevelInfo().getValue().toString())) {
122+
Level level = getLoggerLevel();
123+
if (level != null) {
124+
put(getLevelInfo(), DSFlexEnum.valueOf(level.toString(), levelRange));
125+
} else {
126+
scheduleRetry(retryDelay);
127+
}
128+
} else {
129+
Logger loggerObj = getLoggerObj();
130+
String level = getLevelInfo().getValue().toString();
131+
if (loggerObj != null) {
132+
loggerObj.setLevel(Level.parse(level));
133+
} else {
134+
scheduleRetry(retryDelay);
135+
}
136+
}
137+
}
138+
139+
private void scheduleRetry(final long retryDelay) {
140+
if (retryDelay > 0) {
141+
DSRuntime.runDelayed(new Runnable() {
142+
@Override
143+
public void run() {
144+
updateLevel(retryDelay * 2);
145+
}
146+
}, retryDelay);
147+
}
148+
}
149+
150+
private Level getLoggerLevel() {
151+
Logger obj = getLoggerObj();
152+
if (obj == null) {
153+
return null;
154+
}
155+
Level l = obj.getLevel();
156+
while (l == null) {
157+
obj = obj.getParent();
158+
l = obj.getLevel();
159+
}
160+
return l;
161+
}
162+
163+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.acuity.iot.dsa.dslink.sys.logging;
2+
3+
import java.util.Enumeration;
4+
import java.util.logging.LogManager;
5+
import java.util.logging.Logger;
6+
import org.iot.dsa.node.DSFlexEnum;
7+
import org.iot.dsa.node.DSInfo;
8+
import org.iot.dsa.node.DSList;
9+
import org.iot.dsa.node.DSMap;
10+
import org.iot.dsa.node.DSMetadata;
11+
import org.iot.dsa.node.DSString;
12+
import org.iot.dsa.node.action.ActionInvocation;
13+
import org.iot.dsa.node.action.ActionResult;
14+
import org.iot.dsa.node.action.DSAbstractAction;
15+
16+
public class SysLogService extends StreamableLogNode {
17+
18+
private DSInfo levelInfo = getInfo("Default Log Level");
19+
20+
public SysLogService() {
21+
}
22+
23+
@Override
24+
public Logger getLoggerObj() {
25+
return LogManager.getLogManager().getLogger("");
26+
}
27+
28+
public DSInfo getLevelInfo() {
29+
return levelInfo;
30+
}
31+
32+
@Override
33+
protected void declareDefaults() {
34+
super.declareDefaults();
35+
declareDefault("Add Log", getAddLogAction());
36+
declareDefault("Stream All", getStreamLogAction());
37+
declareDefault("Default Log Level", DSString.valueOf("Unknown")).setTransient(true);
38+
}
39+
40+
@Override
41+
protected void onStable() {
42+
super.onStable();
43+
}
44+
45+
private DSList getLogNames() {
46+
DSList l = new DSList();
47+
Enumeration<String> logNames = LogManager.getLogManager().getLoggerNames();
48+
while (logNames.hasMoreElements()) {
49+
String name = logNames.nextElement();
50+
if (!name.isEmpty()) {
51+
l.add(name);
52+
}
53+
}
54+
return l;
55+
}
56+
57+
58+
private DSAbstractAction getAddLogAction() {
59+
DSAbstractAction act = new DSAbstractAction() {
60+
61+
@Override
62+
public void prepareParameter(DSInfo info, DSMap parameter) {
63+
DSMetadata meta = new DSMetadata(parameter);
64+
if ("Log".equals(meta.getName())) {
65+
DSList range = getLogNames();
66+
if (range.size() > 0) {
67+
meta.setType(DSFlexEnum.valueOf(range.getString(0), range));
68+
}
69+
}
70+
}
71+
72+
@Override
73+
public ActionResult invoke(DSInfo info, ActionInvocation invocation) {
74+
((SysLogService) info.getParent()).addLog(invocation.getParameters());
75+
return null;
76+
}
77+
};
78+
DSList range = getLogNames();
79+
act.addParameter("Log", DSFlexEnum.valueOf(range.getString(0), range), "");
80+
return act;
81+
}
82+
83+
private void addLog(DSMap parameters) {
84+
String logName = parameters.getString("Log");
85+
put(logName, new LoggerNode());
86+
}
87+
88+
}

dslink-v2/src/main/java/org/iot/dsa/dslink/DSSysNode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.acuity.iot.dsa.dslink.protocol.v1.DS1LinkConnection;
44
import com.acuity.iot.dsa.dslink.protocol.v2.DS2LinkConnection;
55
import com.acuity.iot.dsa.dslink.sys.cert.SysCertManager;
6+
import com.acuity.iot.dsa.dslink.sys.logging.SysLogService;
67
import com.acuity.iot.dsa.dslink.sys.profiler.SysProfiler;
78
import org.iot.dsa.node.DSInfo;
89
import org.iot.dsa.node.DSNode;
@@ -24,6 +25,7 @@ public class DSSysNode extends DSNode {
2425
static final String SAVE = "Save";
2526
static final String STOP = "Stop";
2627
static final String PROFILER = "Profiler";
28+
static final String LOGGING = "Logging";
2729

2830
private DSInfo connection = getInfo(CONNECTION);
2931
private DSInfo save = getInfo(SAVE);
@@ -36,6 +38,7 @@ protected void declareDefaults() {
3638
declareDefault(CERTIFICATES, new SysCertManager());
3739
declareDefault(CONNECTION, DSNull.NULL).setTransient(true);
3840
declareDefault(PROFILER, new SysProfiler()).setTransient(true);
41+
declareDefault(LOGGING, new SysLogService());
3942
}
4043

4144
public DSLinkConnection getConnection() {

0 commit comments

Comments
 (0)