Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/reference/ingest/processors/user-agent.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The ingest-user-agent module ships by default with the regexes.yaml made availab
| `regex_file` | no | - | The name of the file in the `config/ingest-user-agent` directory containing the regular expressions for parsing the user agent string. Both the directory and the file have to be created before starting Elasticsearch. If not specified, ingest-user-agent will use the regexes.yaml from uap-core it ships with (see below).
| `properties` | no | [`name`, `major`, `minor`, `patch`, `build`, `os`, `os_name`, `os_major`, `os_minor`, `device`] | Controls what properties are added to `target_field`.
| `ignore_missing` | no | `false` | If `true` and `field` does not exist, the processor quietly exits without modifying the document
| `ecs` | no | `true` | Whether to return the output in Elastic Common Schema format. NOTE: This setting is deprecated and will be removed in a future version.
|======

Here is an example that adds the user agent details to the `user_agent` field based on the `agent` field:
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/migration/migrate_7_0/settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,6 @@ Elastic Stack to handle the indexing part.

[float]
[[ingest-user-agent-ecs-always]]
==== Ingest User Agent processor always uses `ecs` output format
The deprecated `ecs` setting for the user agent ingest processor has been
removed. https://github.com/elastic/ecs[ECS] format is now the default.
==== Ingest User Agent processor defaults uses `ecs` output format
https://github.com/elastic/ecs[ECS] format is now the default.
The `ecs` setting for the user agent ingest processor now defaults to true.
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
import org.elasticsearch.ingest.useragent.UserAgentParser.Details;
import org.elasticsearch.ingest.useragent.UserAgentParser.VersionedName;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand All @@ -51,15 +53,17 @@ public class UserAgentProcessor extends AbstractProcessor {
private final Set<Property> properties;
private final UserAgentParser parser;
private final boolean ignoreMissing;
private final boolean useECS;

public UserAgentProcessor(String tag, String field, String targetField, UserAgentParser parser, Set<Property> properties,
boolean ignoreMissing) {
boolean ignoreMissing, boolean useECS) {
super(tag);
this.field = field;
this.targetField = targetField;
this.parser = parser;
this.properties = properties;
this.ignoreMissing = ignoreMissing;
this.useECS = useECS;
}

boolean isIgnoreMissing() {
Expand All @@ -80,68 +84,135 @@ public IngestDocument execute(IngestDocument ingestDocument) {

Map<String, Object> uaDetails = new HashMap<>();

// Parse the user agent in the ECS (Elastic Common Schema) format
for (Property property : this.properties) {
switch (property) {
case ORIGINAL:
uaDetails.put("original", userAgent);
break;
case NAME:
if (uaClient.userAgent != null && uaClient.userAgent.name != null) {
uaDetails.put("name", uaClient.userAgent.name);
} else {
uaDetails.put("name", "Other");
}
break;
case VERSION:
StringBuilder version = new StringBuilder();
if (uaClient.userAgent != null && uaClient.userAgent.major != null) {
version.append(uaClient.userAgent.major);
if (uaClient.userAgent.minor != null) {
version.append(".").append(uaClient.userAgent.minor);
if (uaClient.userAgent.patch != null) {
version.append(".").append(uaClient.userAgent.patch);
if (uaClient.userAgent.build != null) {
version.append(".").append(uaClient.userAgent.build);
if (useECS) {
// Parse the user agent in the ECS (Elastic Common Schema) format
for (Property property : this.properties) {
switch (property) {
case ORIGINAL:
uaDetails.put("original", userAgent);
break;
case NAME:
if (uaClient.userAgent != null && uaClient.userAgent.name != null) {
uaDetails.put("name", uaClient.userAgent.name);
} else {
uaDetails.put("name", "Other");
}
break;
case VERSION:
StringBuilder version = new StringBuilder();
if (uaClient.userAgent != null && uaClient.userAgent.major != null) {
version.append(uaClient.userAgent.major);
if (uaClient.userAgent.minor != null) {
version.append(".").append(uaClient.userAgent.minor);
if (uaClient.userAgent.patch != null) {
version.append(".").append(uaClient.userAgent.patch);
if (uaClient.userAgent.build != null) {
version.append(".").append(uaClient.userAgent.build);
}
}
}
uaDetails.put("version", version.toString());
}
uaDetails.put("version", version.toString());
}
break;
case OS:
if (uaClient.operatingSystem != null) {
Map<String, String> osDetails = new HashMap<>(3);
if (uaClient.operatingSystem.name != null) {
osDetails.put("name", uaClient.operatingSystem.name);
StringBuilder sb = new StringBuilder();
if (uaClient.operatingSystem.major != null) {
sb.append(uaClient.operatingSystem.major);
if (uaClient.operatingSystem.minor != null) {
sb.append(".").append(uaClient.operatingSystem.minor);
if (uaClient.operatingSystem.patch != null) {
sb.append(".").append(uaClient.operatingSystem.patch);
if (uaClient.operatingSystem.build != null) {
sb.append(".").append(uaClient.operatingSystem.build);
break;
case OS:
if (uaClient.operatingSystem != null) {
Map<String, String> osDetails = new HashMap<>(3);
if (uaClient.operatingSystem.name != null) {
osDetails.put("name", uaClient.operatingSystem.name);
StringBuilder sb = new StringBuilder();
if (uaClient.operatingSystem.major != null) {
sb.append(uaClient.operatingSystem.major);
if (uaClient.operatingSystem.minor != null) {
sb.append(".").append(uaClient.operatingSystem.minor);
if (uaClient.operatingSystem.patch != null) {
sb.append(".").append(uaClient.operatingSystem.patch);
if (uaClient.operatingSystem.build != null) {
sb.append(".").append(uaClient.operatingSystem.build);
}
}
}
osDetails.put("version", sb.toString());
osDetails.put("full", uaClient.operatingSystem.name + " " + sb.toString());
}
osDetails.put("version", sb.toString());
osDetails.put("full", uaClient.operatingSystem.name + " " + sb.toString());
uaDetails.put("os", osDetails);
}
uaDetails.put("os", osDetails);
}
}
break;
case DEVICE:
Map<String, String> deviceDetails = new HashMap<>(1);
if (uaClient.device != null && uaClient.device.name != null) {
deviceDetails.put("name", uaClient.device.name);
} else {
deviceDetails.put("name", "Other");
}
uaDetails.put("device", deviceDetails);
break;
break;
case DEVICE:
Map<String, String> deviceDetails = new HashMap<>(1);
if (uaClient.device != null && uaClient.device.name != null) {
deviceDetails.put("name", uaClient.device.name);
} else {
deviceDetails.put("name", "Other");
}
uaDetails.put("device", deviceDetails);
break;
}
}
} else {
// Deprecated format, removed in 8.0
for (Property property : this.properties) {
switch (property) {
case NAME:
if (uaClient.userAgent != null && uaClient.userAgent.name != null) {
uaDetails.put("name", uaClient.userAgent.name);
} else {
uaDetails.put("name", "Other");
}
break;
case MAJOR:
if (uaClient.userAgent != null && uaClient.userAgent.major != null) {
uaDetails.put("major", uaClient.userAgent.major);
}
break;
case MINOR:
if (uaClient.userAgent != null && uaClient.userAgent.minor != null) {
uaDetails.put("minor", uaClient.userAgent.minor);
}
break;
case PATCH:
if (uaClient.userAgent != null && uaClient.userAgent.patch != null) {
uaDetails.put("patch", uaClient.userAgent.patch);
}
break;
case BUILD:
if (uaClient.userAgent != null && uaClient.userAgent.build != null) {
uaDetails.put("build", uaClient.userAgent.build);
}
break;
case OS:
if (uaClient.operatingSystem != null) {
uaDetails.put("os", buildFullOSName(uaClient.operatingSystem));
} else {
uaDetails.put("os", "Other");
}

break;
case OS_NAME:
if (uaClient.operatingSystem != null && uaClient.operatingSystem.name != null) {
uaDetails.put("os_name", uaClient.operatingSystem.name);
} else {
uaDetails.put("os_name", "Other");
}
break;
case OS_MAJOR:
if (uaClient.operatingSystem != null && uaClient.operatingSystem.major != null) {
uaDetails.put("os_major", uaClient.operatingSystem.major);
}
break;
case OS_MINOR:
if (uaClient.operatingSystem != null && uaClient.operatingSystem.minor != null) {
uaDetails.put("os_minor", uaClient.operatingSystem.minor);
}
break;
case DEVICE:
if (uaClient.device != null && uaClient.device.name != null) {
uaDetails.put("device", uaClient.device.name);
} else {
uaDetails.put("device", "Other");
}
break;
}
}
}

Expand Down Expand Up @@ -201,6 +272,10 @@ UserAgentParser getUaParser() {
return parser;
}

public boolean isUseECS() {
return useECS;
}

public static final class Factory implements Processor.Factory {

private final Map<String, UserAgentParser> userAgentParsers;
Expand All @@ -217,10 +292,7 @@ public UserAgentProcessor create(Map<String, Processor.Factory> factories, Strin
String regexFilename = readStringProperty(TYPE, processorTag, config, "regex_file", IngestUserAgentPlugin.DEFAULT_PARSER_NAME);
List<String> propertyNames = readOptionalList(TYPE, processorTag, config, "properties");
boolean ignoreMissing = readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
Object ecsValue = config.remove("ecs");
if (ecsValue != null) {
deprecationLogger.deprecated("setting [ecs] is deprecated as ECS format is the default and only option");
}
boolean useECS = readBooleanProperty(TYPE, processorTag, config, "ecs", true);

UserAgentParser parser = userAgentParsers.get(regexFilename);
if (parser == null) {
Expand All @@ -242,22 +314,53 @@ public UserAgentProcessor create(Map<String, Processor.Factory> factories, Strin
properties = EnumSet.allOf(Property.class);
}

return new UserAgentProcessor(processorTag, field, targetField, parser, properties, ignoreMissing);
if (useECS == false) {
deprecationLogger.deprecated("setting [ecs] to false for non-common schema " +
"format is deprecated and will be removed in 8.0, set to true or remove to use the non-deprecated format");
}

return new UserAgentProcessor(processorTag, field, targetField, parser, properties, ignoreMissing, useECS);
}
}

enum Property {

NAME,
// Deprecated in 6.7 (superceded by VERSION), to be removed in 8.0
@Deprecated MAJOR,
@Deprecated MINOR,
@Deprecated PATCH,
OS,
// Deprecated in 6.7 (superceded by just using OS), to be removed in 8.0
@Deprecated OS_NAME,
@Deprecated OS_MAJOR,
@Deprecated OS_MINOR,
DEVICE,
@Deprecated BUILD, // Same deprecated as OS_* above
ORIGINAL,
VERSION;

private static Set<Property> DEPRECATED_PROPERTIES;

static {
Set<Property> deprecated = new HashSet<>();
for (Field field : Property.class.getFields()) {
if (field.isEnumConstant() && field.isAnnotationPresent(Deprecated.class)) {
deprecated.add(valueOf(field.getName()));
}
}
DEPRECATED_PROPERTIES = deprecated;
}

public static Property parseProperty(String propertyName) {
try {
return valueOf(propertyName.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
Property value = valueOf(propertyName.toUpperCase(Locale.ROOT));
if (DEPRECATED_PROPERTIES.contains(value)) {
deprecationLogger.deprecated("the [{}] property is deprecated for the user-agent processor", propertyName);
}
return value;
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("illegal property value [" + propertyName + "]. valid values are " +
Arrays.toString(EnumSet.allOf(Property.class).toArray()));
}
Expand Down
Loading