Press "Enter" to skip to content

Variant CVM Server Reference

Release 0.10.3, December 2019

1. Variant Server Installation

Variant server is entirely self-contained and does not have any external dependencies. It requires Java runtime 8 or later. To install Variant server:

  • Download Variant Server distribution.
  • Unpack the distribution in a directory of your choice by running
$ unzip /path/to/variant-server-<release>.zip

This will create a server installation with the following directory structure:

DirectoryDescription
bin/Contains the server startup shell script variant.sh along with dependent scripts which you should never have to use.
conf/Configuration directory, containing the server config file variant.conf, logging config file logback.xml, and other dependent configuration artifacts.
lib/System libraries.
ext/User extensions, developed on top of the Extension API, and their dependencies. The distribution contains the the standard extension library variant-extapi-standard-<release>.jar. All JAR files in the ext/ directory are added to Variant server’s runtime classpath.
log/Default destination for the server log files.
schemata/Default location of the variation schema files. The distribution contains the example.schema file.
  • Change directory to Variant server top level directory:
$ cd variant-server-<release>
  • Start Variant server in the foreground:
$ bin/variant.sh start

If all goes well, the server console output should look something like this:

$ bin/variant.sh start
2019-12-10 09:54:44,477 INFO - c.v.s.boot.ConfigLoader$ - Found  config resource [/variant.conf] as [/Users/Igor/soft/variant-server-0.10.2/conf/variant.conf]
2019-12-10 09:54:45,285 INFO - c.v.s.schema.SchemaDeployerFileSystem - Mounted schemata directory [/Users/Igor/soft/variant-server-0.10.2/schemata]
2019-12-10 09:54:45,287 INFO - c.v.s.schema.SchemaDeployerFileSystem - [421] Deploying schema from file [/Users/Igor/soft/variant-server-0.10.2/schemata/example.schema]
2019-12-10 09:54:45,490 INFO - c.v.s.schema.Schemata - [422] Deployed schema [exampleSchema] from file [example.schema]
2019-12-10 09:54:45,494 INFO - c.v.s.boot.VariantServerImpl - [433] Variant CVM Server release 0.10.2 started on port [5377] in 2.131s

At this point, the server is ready to deploy variation schema files, which you copy into the schemata/ directory.

By default, the server attaches to port 5377. To start the server on a different port, pass the port number in the variant.http.port on the startup command line. For example, to start on port 9999:

$ bin/variant.sh start -Dvariant.http.port=9999

You may always ping a running server by typing its URL into a Web browser, or with the curl command:

$ curl localhost:5377
{
  "name" : "Variant CVM Server",
  "version" : "0.10.3",
  "uptimeSeconds" : 6,
  "build" : {
    "timestamp" : "2019-12-22T20:02:10.937Z",
    "scalaVersion" : "2.12.7",
    "javaVersion" : "1.8.0_191",
    "javaVmName" : "Java HotSpot(TM) 64-Bit Server VM",
    "javaVmVersion" : "25.191-b12"
  },
  "schemata" : [ {
    "name" : "example",
    "comment" : "Basic sample variation schema. See User Guide for details."
  } ]
}

A running server can be stopped by typing Control-C in the shell where it was started, or by typing in a different shell window

$ bin/variant.sh stop

2. Variant Server Configuration

2.1. Sources Of Configuration

Variant server is configured via configuration properties organized in conf files. At startup, Variant server looks for configuration in the file conf/variant.conf. If it is found, its contents override the default settings, listed in the next section. If you need to modify any configuration properties, you may either update them directly in the conf/variant.conf file, or provide an alternate configuration file whose contents take precedence over those of the conf/variant.conf file.

The alternate config file may be provided on the command line either as a file system file:

$ variant.sh start -Dvariant.config.file=/path/to/alt/config/as/file

or as a Java classpath resource:

$ variant.sh start -Dvariant.config.resource=resource-name

The simplest way to add a resource to the server classpath is to place it in the server’s conf directory, which is added to the server’s runtime classpath at the root level. In other words, the file conf/extra.conf should be referenced as

$ variant.sh start -Dvariant.config.resource=/extra.conf

It is an error to set both variant.config.file and variant.config.resource system properties.

Finally, each individual config parameter may be overridden from the command line via the JVM system variable of the same name, e.g.:

$ variant.sh start -Dvariant.schemata.dir=/Users/Igor/schemata

2.2. Variant Server Configuration Properties

The following table lists all config properties recognized by Variant server.

PropertyDefault Value / Description
variant.event.flusher.class.namecom.variant.server.api.EventFlusherAppLogger Default event flusher implementation, implicitly provided to those variation schemata that do not define their own.
variant.event.flusher.class.initnull Arbitrary JSON object, whose parsed representation will be passed to the constructor of the event flusher implementation. Not required by the default com.variant.server.api.EventFlusherAppLogger above.
variant.event.writer.flush.buffers[10,20] Min and max number of flush buffers in the event writer buffer cache. The first value is the number of buffers which will be always kept around even if fewer would suffice to keep up with the trace event production rate. Whenever event writer requires more flush buffers than is currently allocated (due to an increased rate of production or deployment of new schema(ta)) additional buffers will be allocated up to the second number. Whenever the demand for flush buffers drops, additional flush buffers will be deallocated until the total number of buffers reaches the first number again. The second value must be >= the first value.
variant.event.writer.flush.buffer.size500 Number of trace events each event buffer can hold.
variant.event.writer.max.delay30 Maximum delay in seconds between the time when a trace event is triggered and the time it is passed to an event flusher. This ensures that events triggered by a low-traffic schema and not stuck in the asynchronous event writer for exessively long periods of time.
variant.event.writer.flush.pool.sizeTwice the number of available cores. The size of the flush thread pool. Event writer schedules calls to the flush() methods to be executed concurrently in a dedicated thread pool of this size. Buffers are flushed in the order they fill up. The flush pool is shared between all schemata.
variant.http.port5377 Variant server HTTP port.
variant.https.port5378 Variant server HTTPS port.
variant.schemata.dir"schemata" The directory where Variant server looks for variation schemata, as an OS file name. If starts with a slash, interpreted as an absolute path. Otherwise, as relative path to the server startup directory.
variant.session.timeout900 Session timeout. User sessions are expired after this many seconds of inactivity.
variant.session.vacuum.interval10 The session vacuum thread is woken up no less frequently than this many seconds.

3. Variation Schema Grammar

Variant manages code variation metadata in human readable files, called schema files. Each schema file contains a single code variation schema describing a set of related code variations instrumented on some host application using the familiar YAML syntax. Keys (the strings to the left of the :) are the reserved keywords that have a specific meaning. Keywords are case-insensitive, e.g. 'name:', 'Name:', or 'NAME:' are interhcangeable.

The rest of this chapter provides detailed information on the schema grammar. The following conventions are used throughout the chapter:

any-stringArbitrary, case sensitive, Unicode string. Follow YAML’s escape rules if you want a string contain special characters.
name-stringCase insensitive, quoted string containing only Unicode letters, digits or the ‘_’ (underscore) and not starting with a digit. For example, _mySchema is a valid name and is indistinguishible from, e.g. _MYSCHEMA, but 3rdField is not a valid name.
booleanYAML Boolean value of true or false.
numberYAML numeric value. Numbers without a decimal point will be converted to a 32-bit signed integer. Numbers containing a decimal point will be converted to a 64 bit double.
literalArbitrary YAML literal. This may be a primitive type, an object, or an array.
{type}YAML mapping (dictionary) literal of a given type.
[type]YAML sequence of mappings of a given type.
key?:Optional key.

3.1. General Structure

A variation schema is a dictionary with the following structure:

schema-spec :=
  name: name-string
  description?: any-string
  states: [state-spec]
  variations: [variation-spec]
  flusher?: <flusher-spec>
  hooks?: [hook-spec]

Listing 1. Top-level structure of a variation schema config.

PropertyTypeDescriptionRequiredDefault
namename-stringThe name by which this schema will be connected to by clients.Yes
descriptionany-stringA comment about this schema.NoNone
flusherMapping of type flusher-specAn event flusher specification defines a schema-specific trace event flusher. Applies to all trace events generated by the variations configured by this schema.NoAs configured by the server config properties.
hooksSequence of mappings of type hook-specA list of schema-scoped lifecycle hook specifications. Hooks defined at this scope apply to all states and all variations defined by this schema.No
statesSequence of mappings of type state-spec.A list of state specifications. Yes
variationsSequence of mappings of type variation-spec.A list of variation specifications.Yes

3.2. Trace Even Flusher

Each Variant schema gets its own instance of an event flusher, shared by all variations. Flusher is instantiated by Variant server at schema deployment time. The Java class referenced by the class property must be on the server’s class path. You may pass arbitrary state data to this instance by supplying the init property, which must be a string. If no init string is given, the flusher will be instantiated with a public nullary constructor. If an init string is given, it will be passed to the public unary constructor which takes a single parameter of type String. The appropriate constructor must be provided by the implementing class.

flusher-spec :=
  name?: name-string
  class: any-string
  init?: any-string
PropertyTypeDescriptionRequiredDefault
namename-stringThe name by which this flusher will be referenced in system messages.NoThe short name of the class.
classany-stringFully qualified name of the implementing class.Yes
initany-stringFlusher’s initial state, passed to its constructor.NoNone

For more information, see Section 4.2 Event Flushers.

3.3. Lifecycle Hooks

Lifecycle hooks are custom callbacks that can be defined at the schema, state, or variation scope. Each hook listens to a particular lifecycle event, such as VariationQualificationLifecycleEvent. Whenever the event is raised, Variant posts all qualifying hooks in a predictable order. For more information, see Section 4.1 Lifecycle Hooks.

The Java class referenced by the class property must be on the server’s class path. You may pass arbitrary state data to this instance by supplying the init property, which must be a string. If no init string is given, the hook will be instantiated with a public nullary constructor. If an init string is given, it will be passed to the public unary constructor which takes a single parameter of type String. The appropriate constructor must be provided by the implementing class.

hook-spec :=
  name?: name-string
  class: any-string
  init?: any-string
PropertyTypeDescriptionRequiredDefault
namename-stringThe name of the hook. Must be unique within current scope.NoShort name of the class.
classany-stringFully qualified name of the implementing class.Yes
initany-stringHook’s initial state, passed to its constructor.NoNone.

3.4. SATES Mapping

In the Code Variation Model, a state represents an interface state of the host application where it pauses for user input. CVM states are rather abstract: the only required property is name, by which the state can be referenced. States form a set, not a graph, i.e. user sessions traverse states one at a time, but there is no predefined order in which these states can be visited or which states are accessible from any particular state.

state-spec :=
  name: name-string
  parameters?: <state-parameter-spec>
  hooks: [hook-spec]
PropertyTypeDescriptionRequiredDefault
namename-stringThe name of the state. Must be unique within a schema.Yes
parametersAn mapping of type state-parameter-specEach field in the object represents a state parameter.NoNone
hooksArray of objects of type hook-specA list of state-scoped lifecycle hook specifications. Hooks defined at this scope apply to this state only and must be subscribed to a lifecycle event descendant from StateAwareLifecycleEvent .NoNone

3.5. State Parameters

Although CVM is agnostic of the host application’s technology stack, it provides a mechanism for the test designer to attach application-specific context to states in the form of state parameters. The host application can access this externalized state at run time. State parameters can be specified either at the state level, as explained in the previous section, or at the state variant level, as explained later in Section 3.7.3. Each state parameter is a key/value pair, where both the key and the value are strings.

state-parameter-spec :=
  any-string: any-string
  ...
PropertyTypeDescriptionRequiredDefault
Parameter’s nameany-stringParameter’s name is an any-string that is a valid YAML key. Any number of parameters can be specified.Yes
Parameter’s value.any-stringParameter’s value is any-string .Yes

Whenever the host application calls the Session.targetForState() method, server picks from the given state’s variant space a single state variant. The state parameters defined at that state variant, if any, override the like-named parameters defined at the base state. (Remember, names are case sensitive.) These resolved state parameters are available to the host application via the StateRequest.getResolvedParameters() method.

3.6. VARIATIONS Mapping

3.6.1. Code Variations

A code variation or just variation is is a point code-path bifurcation, where the actual code path is determined dynamically at runtime. Variations are instrumented over one or more states and contain two or more experiences. Each variation must define exactly one control experience (the one mapped to the current code path) and one or more variant experiences, mapped to the candidate code path(s).

variation-spec :=
  name: name-string
  isOn?: boolean
  durability?: durability-spec
  experiences: [experience-spec]
  concurrentVariations: [name-string]
  onStates: [on-state-spec]
  hooks?: [hook-spec]
PropertyTypeRequiredDefault
namename-stringYes
isOnbooleanNotrue
durabilityAn mapping of type durability-specNoSession durability for both qualification and targeting.
experiencesSequence of mappings of type experience-specYes
concurrentVariationsSequence of name-strings.No
onStatesSequence of mappings of type on-state-specYes
hooksSequence of mappings of type hook-specNo

For each state on the onStates list, Variant builds a state variant space as a Cartesian product of this variation’s experience set and the experience sets of all those variations mentioned on the conjointVariations list. Refer to Variant Server User Guide for more information on concurrent variations.

Each variation-scoped hook must listen to a lifecycle event descendant from VariationAwareLifecycleEvent . For more information, see Section 4.1 Lifecycle Hooks.

The isOn property is used to turn an experiment or a feature toggle temporarily offline without removing it from the schema. No sessions are targeted for an offline variation, as if it didn’t even exist. In fact, the only differences between an offline variation and a variation that is completely removed from the schema is that if it defines its targeting or qualification persistence as durable, this information is preserved. In practice this means that, after an offline variation is taken back online, return users will see the same experience they saw before the variation was taken offline.

3.6.2. Variation Durability

Variation durability determines the degree of persistence of qualification and targeting decisions with respect to the given variation.

durability-spec :=
  qualification: state|session|variation
  targeting: state|session|variation

3.6.3. Variation Experiences

Each element of a variation’s experiences property describes one of its experiences.

experience-spec :=
  name: name-string
  weight: number
  isControl?: boolean
PropertyTypeDescriptionRequiredDefault
namename-stringThe name of the experience. Must be unique within enclosing variation.Yes
weightnumberThe probabilistic weight of this experience relative to other experiences of this variation. Used by the default targeting hook, which targets sessions randomly, according to these weights. The probability that a session is targeted to an experience is the ratio of the weight of the experience to the sum of weights of all of the variation’s experiences.No1
isControlbooleanIndicates whether this experience is the control experience in the enclosing variation. Exactly one experience must be marked as control.Nofalse

Listing 8. The schema definition of a test experience.

3.6.4. Variation On-States

The onStates property contains a list of elements, each of which describes the enclosing variation’s instrumentation details on a particular state.

on-state-spec :=
  state: name-string
  experiences?: [name-string]
  variants?: [state-variant-spec]

Listing 9 The schema definition of an onState property.

PropertyTypeDescriptionRequiredDefault
statename-stringBack reference to a state that’s already been defined, whose instrumentation by the enclosing test is defined by this property.Yes
experiencesArray of name-strings.This variation’s experiences that are instrumented on this state. NoList of this state’s experiences.
varaintsArray of objects of type state-variant-specA list of this state’s variant specifications.No

Whenever a variation V instruments state S, Variant schema parser creates the state variant space ν(V,S) as a Cartesian product of the set of V‘s experiences and the experience sets of all variations conjointly concurrent with V. All state variants in ν(V,S) implicitly inherit the state parameters as defined in the base state S. In most cases, this inferred state variant space is sufficient, and you will only need to define state variants explicitly if you wish to override one or more state variants.

Implicitly inferred state variants inherit their state parameters from the base state. If you need to define variant-specific parameters, you must define them explicitly. Refer to Server User Guide for more information on state parameter inheritance.

3.6.5. State Variants

The variants property contains a list of elements, each of which describes a particular state variant.

state-variant-spec :=
  isPhantom?: boolean
  experience: name-string
  concurrentExperiences?:
    - variation: name-string
      experience: name-string
    ...
  parameters: state-parameter-spec
PropertyTypeDescriptionRequiredDefault
isPhantombooleanIndicates whether this state variant is phantom. If set to true, incompatible with any other property.Nofalse
experiencename-stringReference to one of the enclosing variation’s own variant experiences. Cannot refer to the control experience. Not allowed in phantom state variants.Yes
conjointExperiencesArray of objects.Reference to one of the enclosing variation’s conjoint experiences. Not allowed in phantom state variants.Yes
parametersArray of objects of type state-parameter-spec.A list of state parameter specifications. Not allowed in phantom state variants.No

Listing 10. The schema definition of a state variant.

A state variant is phantom in a particular experience if it is not instrumented by the experience. Phantom state variants possess the following semantics:

  • When a session is targeted for a variation, phantom experiences are not considered.
  • When a session requests a state which is phantom in a live experience, Variant will emit a runtime user error.

Refer to Variant Server User Guide for more information on mixed instrumentation.

4. Server Extension API

Variant server’s functionality can be extended through the use of the server-side Extension API, or ExtAPI, which exposes Java bindings for supplying custom server-side extensions. These extensions facilitate injection of custom semantics into the server’s default execution path via a callback mechanism. Two types of user-defined callback objects are supported:

  • Lifecycle Event Hooks are listeners for various lifecycle events, raised by the server. They encapsulate custom, application-aware code, which takes over and alters the default handling of lifecycle events.
  • Trace Event Flushers handle the final ingestion of trace events. Each variation schema can have its own event flusher.

Both lifecycle hooks and event flushers are configured in the variation schema. The next two chapters explain their semantics and configuration, and chapter 4.3 provides details on how to write them.

4.1. Lifecycle Hooks

4.1.1. Lifecycle Events

The following is the complete list of lifecycle events raised by Variant server:

Lifecycle EventEvent ScopeDefault Hook
VariationQualificationLifecycleEvent Schema VariationSession is qualified for the variation.
Raised when a Variant session must be qualified for a variation. Posts all eligible schema-scoped hooks and those variation-scoped hooks whose variation matches that of triggering event. Used to qualify (or disqualify) a user session for the triggering variation, based on a custom qualification criteria.
VariationTargetingLifecycleEvent Schema State VariationVariation is targeted randomly, according to the weight properties.
Raised when a Variant session must be targeted for a variation. Posts all eligible schema-scoped hooks, those state-scoped hooks whose state matches that of triggering event, and those variation-scoped hooks whose variation matches that of the triggering event. Used to provide custom targeting algorithm.

4.1.2. Hooks’ Instantiation and Scope

Lifecycle hooks provide callback methods which are posted by Variant server whenever a lifecycle event of interest is raised. A lifecycle hook subscribes to a particular lifecycle event type by implementing the LifecycleHook.gerLifecycleEventClass()  method. Whenever an event of that type is raised, the hook is notified by Variant server via its callback method LifecycleHook.post()  method.

Hook definitions may appear in variation schema in one of three scopes:

  • Meta-scoped hooks are defined inside the meta section. These hooks are applicable to all states and all variations in the schema. Hooks listening to lifecycle events descendant from StateAwareLifecycleEvent  will be posted for each state in the schema by every state aware lifecycle event. Hooks listening to lifecycle events descendant from VariationAwareLifecycleEvent  will be posted for all online variations in the schema by every variation aware lifecycle event.
  • State-scoped hooks are defined inside the state definition. These hooks are applicable to the enclosing state only and must listen to events descendant from StateAwareLifecycleEvent .
  • Variation-scoped hooks are defined inside the variation definition. These hooks are applicable to the enclosing variation only and must listen to events descendant from VariationAwareLifecycleEvent .

4.1.3. Hook Chaining

In any scope, any number of hooks can be defined. If more than one lifecycle hook is eligible to be posted by a lifecycle event at runtime, they form a hook chain.

A hook chain is posted in the following order:

  • Variation-scoped hooks, then state-scoped hooks, then schema-scoped hooks.
  • Within a scope, hooks are posted in the ordinal order, i.e. the order in which they are defined in the schema, in the corresponding scope.

The hooks are posted serially, until a hook’s post() method returns a non-empty Optional. If no custom hooks have been defined for a lifecycle event, or all returned an empty Optional, the default built-in hook for the event is posted, which is guaranteed to return a usable value.

A hook chain is posted synchronously; the hooks’ post() methods are invoked one at a time while the foreground user session thread is blocked. Conequently, the post() method need not be thread safe. A new instance of a hook is instantiated for each invocation of the post() method.

When a hook is posted, its post(E event)  method is called by Variant server with the actual triggering lifecycle event instance. If the post(E event) method returns a non-null value, Variant server ignores the rest of the hook chain, expecting the returned object to contain the information it requires to proceed. Otherwise, Variant posts the next hook on the chain.

4.1.4. Custom Lifecycle Hooks

A custom lifecycle hook must implement the LifecycleHook  interface. By contract, an implementation must also provide at least one of these constructors:

  • Nullary constructor, if no init property was given in the hook definition.
  • Single argument constructor with argument type Config . If no init property was given and no nullary constructor is available, this constructor will be called with null argument; otherwise, the value of the init property will be parsed and passed to this constructor.

Refer to Section 4.3 for packaging details.

4.2. Trace Event Flushers

Trace events are generated by user traffic, as it flows through Variant variations. Event flushers handle the terminal ingestion of these events with the purpose of subsequent analysis by a downstream process. Trace events can be triggered implicitly, by Variant, or explicitly by the host application. In either case, the host application can attach attributes to these events, to aid in the downstream analysis.

Variant server automatically enriches all trace events with the following metadata:

  • Variant session ID by which related events can be associated.
  • Names of sessions’s live experiences.
  • Custom event attributes.

A typical event flusher writes trace events to a persistent storage mechanism, such as an external database or event stream. Whenever a trace event is triggered — implicitly by Variant server or explicitly by user code — it is picked up by the Variant’s asynchronous event writer, where it is held in a memory buffer until a dedicated flusher thread becomes available. There is one event writer per Variant server, shared by all schemata. Event writer groups trace events by the schema that produced them and turns them over to the apropriate event flusher by calling its flush() method.

Each call to the flush() method is scheduled to be executed asynchronously and, potentially concurrently with other similar calls, by a dedicated thread pool, whose size is configured by the variant.event.writer.buffer.flush.pool.size config property. Buffers are passed to the thread pool as soon as they fill up or the variant.event.writer.max.delay number of seconds has elapsed since the time when the oldest event in the buffer was triggered.

Note, that it is possible that the same flusher instance is accessed by multiple threads, so it is critical that the flush() method you write be thread safe. In particular, avoid mutable instance state in custom event flushers.

Trace event flushers are configured in the META section of the variation schema, as in the following example:

{
   'meta':{
      'name':'petclinic',
      'comment':'Variant schema for the Pet Clinic demo application',
      'flusher': {
        'class':'com.variant.extapi.std.flush.TraceEventFlusherCsv',
        'init':{'header':true, 'file':'/tmp/variant-trace-events-petclinic.csv'}
       }
   },

   'states':[...],
   'variations':[...],
}

If no event flusher is configured in the schema, the system wide default is assumed, as configured by the variant.event.flusher.class.* properties.

A number of pre-built trace event flushers come with the server, as part of the standard extension library, discussed in Section 4.4.

4.3. Developing for the ExtAPI

Variant server extension API library variant-server-extapi-<release>.jar can be downloaded from the Variant website. Create a new Java project in your IDE, add this JAR file to the classpath and you are set. You must package your project as JAR. To add the packaged JAR file to Variant server’s runtime classpath, copy it (and its dependencies) into the server’s ext/ directory.

Alternatively, but with the same ultimate result, — you may clone the standard extension public repository  into a local workspace, remove the source files, change the groupId, artifactId and the version  to suit your environment,— and you have a working shell of a brand new ExtAPI development project.

Note, that a running Variant server loads all hook and flusher classes from the class path only once, when they are first encountered. Therefore, replacing classes in the server’s ext/ directory will not have any effect and may even lead to unexpected behavior. Always restart your Variant server when you redeploy your custom ExtAPI classes.

4.4. The Standard Extension Library

Variant server standard extension is a library of general purpose extensions, written on top of the ExtAPI. They provide out-of-the-box functionality which is not part of the core Variant server. They are packaged as variant-extapi-standard-<release>.jar file wich is incliuded in the server distribution in the ext/ directory.

The standard extension library is an open source project, available on GitHub  under the Apache 2 license. You may find it useful to examine the source code, before developing your own custom lifecyle event hooks and trace event flushers.

4.4.1. Package com.variant.extapi.std.demo

Contains the UserQualifyingHook  lifecycle hook, required by the Servlet Demo Application .

4.4.2. Package com.variant.extapi.std.fush

Contains the folloiwng ready to use trace event flushers:

TraceEventFlusherNull 
Discards all trace events. Configuration: None. Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.TraceEventFlusherNull’ }
TraceEventFlusherServerLog 
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. Configuration: level – specifies the logging level to be used. Defaults to ‘INFO’. Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.TraceEventFlusherApplicationLog’, ‘init’:{‘init’:”info”} }
TraceEventFlusherCsv 
Writes trace events to a local CSV file. The output file format conforms to the IETF RFC4180 specification. Configuration: header – boolean – Wether or not to include the metadata header as very first line. The default is false. file – string – The name of the file to write to. Will be overwritten if exists. The default is “variant-events.csv” Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.TraceEventFlusherCsv’, ‘init’:{‘file’:’/tmp/variant-events.csv’,’header’:true} }
jdbc/TraceEventFlusherH2 
Writes Variant events to an H2 database. The SQL scripts required to create the database schema expected by this flusher can be found in db/h2  directory. You must also copy the H2 JDBC driver into Variant server’s ext/ directory. Configuration: url – string – The URL to the H2 database instance. user – string – The database user name. password – string – The database user’s password. Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.jdbc.TraceEventFlusherH2′, ‘init’:{ ‘url’:”jdbc:h2:mem:variant;MVCC=true;DB_CLOSE_DELAY=-1;’, ‘user’:’variant’, ‘password”:’variant’} }
jdbc/TraceEventFlusherMysql 
Writes Variant events to a MySQL database. The SQL scripts required to create the database schema expected by this flusher can be found in db/mysql  directory. You must also copy the MySQL JDBC driver into Variant server’s ext/ directory. Configuration: url – string – The URL to the MySQL database instance. user – string – The database user name. password – string – The database user’s password. Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.jdbc.TraceEventFlusherMysql’, ‘init’:{ ‘url’:”jdbc:mysql://localhost/variant’, ‘user’:’variant’, ‘password”:’variant’} }
jdbc/TraceEventFlusherPostgres 
Writes Variant events to a PostgreSQL database. The SQL scripts required to create the database schema expected by this flusher can be found in db/postgres  directory. You must also copy the Postgres JDBC driver into Variant server’s ext/ directory. Configuration: url – string – The URL to the PostgreSQL database instance. user – string – The database user name. password – string – The database user’s password. Example:‘flusher’: { ‘class’:’com.variant.extapi.std.flush.jdbc.TraceEventFlusherPostgres’, ‘init’:{ ‘url’:”jdbc:postgresql://localhost/variant’, ‘user’:’variant’, ‘password”:’variant’} }