Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Igor Urisman authored and Igor Urisman committed May 28, 2024
1 parent 6f93638 commit 1d44b7a
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 72 deletions.
Binary file modified lib/variant-spi-1.3.1.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@
import java.util.Map;
import java.util.Optional;

import com.variant.share.yaml.YamlMap;
import com.variant.share.yaml.YamlNode;
import com.variant.share.yaml.YamlScalar;
import org.yaml.snakeyaml.Yaml;
import com.variant.share.schema.Variation.Experience;
import com.variant.server.spi.FlushableTraceEvent;
import com.variant.server.spi.TraceEventFlusher;
import org.yaml.snakeyaml.nodes.ScalarNode;

/**
* An implementation of {@link TraceEventFlusher}, which writes trace events to a local CSV file.
* The output file format conforms to the <a href="https://tools.ietf.org/html/rfc4180">IETF RFC4180</a>
*
* Configuration.
* A YAML string containing these properties:
* A required YAML map containing these keys:
* <ul>
* <li><code>header</code> - boolean - Whether or not to include the header as very first line.
* Optional, defaults to <code>true</code>.
Expand All @@ -33,8 +37,10 @@
* Example:<br/>
* <code>
* flusher:
* class: com.variant.spi.stdlib.TraceEventFlusherCsv
* init: '{file: /tmp/variant-events.csv, header: false}'
* class: com.variant.spi.stdlib.flush.TraceEventFlusherCsv
* init:
* file: /tmp/variant-events.csv
* header: false
* </code>
*
* @since 0.10
Expand All @@ -43,10 +49,10 @@ public class TraceEventFlusherCsv implements TraceEventFlusher {

private BufferedWriter out = null;

public TraceEventFlusherCsv(String init) throws Exception {
Map<String, ?> map =
public TraceEventFlusherCsv(YamlNode<?> init) throws Exception {
Map<String, YamlNode<?>> map =
Optional.ofNullable(init)
.map(string->(Map<String,?>)new Yaml().load(string))
.map(node -> ((YamlMap)node).value())
.orElse(Map.of());
out = Files.newBufferedWriter(Paths.get(parseFileName(map)), CREATE, WRITE, TRUNCATE_EXISTING );
if (parseHeader(map)) {
Expand All @@ -55,6 +61,38 @@ public TraceEventFlusherCsv(String init) throws Exception {
}
}

@SuppressWarnings("unchecked")
private Boolean parseHeader(Map<String, YamlNode<?>> map) {
return Optional.ofNullable(map.get("header"))
.map(node -> ((YamlScalar<Boolean>)node).value())
.orElse(true);
}

@SuppressWarnings("unchecked")
private String parseFileName(Map<String, YamlNode<?>> map) {
return Optional.ofNullable(map.get("file"))
.map(node -> ((YamlScalar<String>)node).value())
.orElse("log/trace-events.csv");
}

/**
* Enclose the string in double quotes. If a double quote already occurs in the string,
* double it, as per the RFC
*/
private String quoteString(String raw) {
return "\"" + raw.replaceAll("\\\"", "\"\"") + "\"";
}

private void writeLine(Object...tokens) throws IOException {
boolean first = true;
for (Object token: tokens) {
if (first) first = false;
else out.append(',');
out.append(quoteString(token.toString()));
}
out.append(System.lineSeparator());
}

/**
* Write a bunch of events to file.
*/
Expand Down Expand Up @@ -109,36 +147,4 @@ public void flush(FlushableTraceEvent[] events, int size) throws Exception {
public void destroy() throws Exception {
out.close();
}

private Boolean parseHeader(Map<String, ?> map) {
final var defaultValue = true;
return Optional.ofNullable(map.get("header")).map(token->(boolean)token).orElse(defaultValue);
}

private String parseFileName(Map<String, ?> map) {
final var defaultValue = "log/trace-events.csv";
return Optional.ofNullable(map.get("file")).map(token->(String)token).orElse(defaultValue);
}

/**
* Enclose the string in double quotes. If a double quote already occurs in the string,
* double it, as per the RFC
*/
private String quoteString(String raw) {
return "\"" + raw.replaceAll("\\\"", "\"\"") + "\"";
}

private void writeLine(Object...tokens) throws IOException {
boolean first = true;
for (Object token: tokens) {
if (first) first = false;
else out.append(',');
out.append(quoteString(token.toString()));
}
out.append(System.lineSeparator());
}
public static void main(String[] args) throws Exception {
var flusher = new TraceEventFlusherCsv("{header: true, outFileName: /tmp/foo.bar}");
System.out.println(flusher);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,55 @@
import com.variant.server.spi.FlushableTraceEvent;
import com.variant.server.spi.TraceEventFlusher;
import com.variant.share.schema.Variation.Experience;
import com.variant.share.yaml.YamlMap;
import com.variant.share.yaml.YamlNode;
import com.variant.share.yaml.YamlScalar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;

import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Optional;

/**
* An implementation of {@link TraceEventFlusher}, which appends trace events
* to the application logger. This is the default, out of the box event flusher,
* which is completely independent of the operational environment. Probably not for production use.
* <p>
* Configuration.
* An optional YAML map containing a single key:
* <ul>
* <li><code>level</code> - specifies the logging level to be used. Defaults to 'INFO'.<br/>
* <li><code>level</code> - string -- The logging level to be used.
* </ul>
* Example:<br/>
* <code>variant.event.flusher.class.init = {level="INFO"}</code>
*
* <code>
* flusher:
* class: com.variant.spi.stdlib.flush.TraceEventFlusherServerLog
* init:
* level: Info
* </code>
* If no init is specified, Info is assumed.
*
* @since 0.5
*/
public class TraceEventFlusherServerLog implements TraceEventFlusher {

private static final Logger LOG = LoggerFactory.getLogger(TraceEventFlusherServerLog.class);
private static enum Level {Trace, Debug, Info, Error}
private Level level = Level.Info; // The default.
private final Level level;

public TraceEventFlusherServerLog(String init) {
if (init != null) {
Map<String, ?> initMap = new Yaml().load(init);
level = Level.valueOf((String) initMap.get("level"));
}
/**
* Init is either null, or has a single string
*/
public TraceEventFlusherServerLog(YamlNode<?> init) {
level = Optional.ofNullable(init)
.map(node -> {
var map = ((YamlMap)node).value();
var levelStr = ((YamlScalar<String>)map.get("level")).value();
return Level.valueOf(levelStr);
})
.orElse(Level.Info);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.variant.spi.stdlib.flush.jdbc;


import com.variant.share.yaml.YamlNode;

public class TraceEventFlusherH2 extends TraceEventFlusherJdbc {

public TraceEventFlusherH2(String init) throws Exception {
public TraceEventFlusherH2(YamlNode<?> init) throws Exception {
super(init);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import com.variant.share.schema.Variation.Experience;
import com.variant.server.spi.FlushableTraceEvent;
import com.variant.server.spi.TraceEventFlusher;
import com.variant.share.yaml.YamlMap;
import com.variant.share.yaml.YamlNode;
import com.variant.share.yaml.YamlScalar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -30,7 +33,7 @@ abstract public class TraceEventFlusherJdbc implements TraceEventFlusher {
* The required database schema can be created by the
* {@code create-schema.sql} SQL script, included with Variant server.
* <p>
* Configuration.<br/>You may use the <code>variant.event.flusher.class.init</code> configuration property to pass configuration details to this object.
* Configuration.<br/>You may use the <code>/flusher.init</code> key to pass configuration details to this object.
*
* <ul>
* <li><code>url</code> - specifies the JDBC URL to the database.
Expand All @@ -42,17 +45,15 @@ abstract public class TraceEventFlusherJdbc implements TraceEventFlusher {
*
* @since 0.5
*/
protected TraceEventFlusherJdbc(String init) throws SQLException {
Config config = ConfigFactory.parseString(init);
@SuppressWarnings("unchecked")
protected TraceEventFlusherJdbc(YamlNode<?> init) throws SQLException {

String url = config.getString("url");
Map<String, YamlNode<?>> initMap = ((YamlMap) init).value();
String url = ((YamlScalar<String>)initMap.get("url")).value();
if (url == null) throw new ServerException("Missing configuration property [url]");


String user = config.getString("user");
String user = ((YamlScalar<String>)initMap.get("user")).value();
if (user == null) throw new ServerException("Missing configuration property [user]");

String password = config.getString("password");
String password = ((YamlScalar<String>)initMap.get("password")).value();;
if (password == null) throw new ServerException("Missing configuration property [password]");

Properties props = new Properties();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.variant.spi.stdlib.flush.jdbc;

import com.variant.share.yaml.YamlNode;

public class TraceEventFlusherMysql extends TraceEventFlusherJdbc {
public TraceEventFlusherMysql(String init) throws Exception {
public TraceEventFlusherMysql(YamlNode<?> init) throws Exception {
super(init);
}
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.variant.spi.stdlib.flush.jdbc;

import com.variant.server.spi.TraceEventFlusher;
import com.variant.share.yaml.YamlNode;

/**
* An implementation of {@link TraceEventFlusher}, which writes trace events to an
Expand All @@ -19,7 +20,7 @@
* @since 0.5
*/
public class TraceEventFlusherPostgres extends TraceEventFlusherJdbc {
public TraceEventFlusherPostgres(String init) throws Exception {
public TraceEventFlusherPostgres(YamlNode<?> init) throws Exception {
super(init);
}
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package com.variant.spi.stdlib.lifecycle;
package com.variant.spi.stdlib.hook;

import com.variant.server.boot.ServerExceptionInternal;
import com.variant.server.spi.TargetingLifecycleEvent;
import com.variant.server.spi.TargetingLifecycleHook;
import com.variant.share.schema.State;
import com.variant.share.schema.Variation;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import com.variant.share.yaml.YamlMap;
import com.variant.share.yaml.YamlNode;
import com.variant.share.yaml.YamlScalar;

import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;

/**
* Targets randomly, honoring weights, which must be supplied in experience properties like so:
Expand Down Expand Up @@ -44,12 +41,16 @@ public class WeightedRandomTargetingHook implements TargetingLifecycleHook {
public WeightedRandomTargetingHook() {
propName = "weight";
}
public WeightedRandomTargetingHook(Node node) {
this.propName = ((MappingNode)node).getValue().stream()
.filter(nt -> ((ScalarNode)nt.getKeyNode()).getValue().equals("key"))
.map(nt -> ((ScalarNode)nt.getValueNode()).getValue())
.findAny()
.orElseThrow(() -> new RuntimeException("Unable to parse init " + node));
public WeightedRandomTargetingHook(YamlNode<?> node) {
var valOpt = Optional.ofNullable(((YamlMap) node).value().get("key"));
propName = valOpt
.map(scalar -> ((YamlScalar<String>)scalar).value())
.orElseThrow(
() ->
new RuntimeException(
"Unable to value [%s] to a string literal"
.formatted("foo"))
);
}
private static Random rand = new Random();

Expand Down

0 comments on commit 1d44b7a

Please sign in to comment.