Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Elasticsearch JSON logs ECS compliant #47105

Merged
merged 63 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
cb24964
refactor ESLogMessage and overriding fields
pgomulka Sep 13, 2019
6b450a9
rename esmessagefields to overrideFields
pgomulka Sep 13, 2019
b23ffb1
javadoc
pgomulka Sep 13, 2019
7f7ff01
draft not working
pgomulka Sep 6, 2019
eb59b65
working logs
pgomulka Sep 20, 2019
3a867c3
Merge branch 'master' into feature/ecs-log-lib
pgomulka Oct 11, 2019
14c793d
Merge branch 'master' into feature/ecs-log-lib
pgomulka Nov 22, 2019
9efb066
make it compile
pgomulka Nov 22, 2019
b0b87a9
make tests work - not passing yet
pgomulka Nov 22, 2019
f5a8b09
unused import
pgomulka Nov 25, 2019
71e555e
some tests passing
pgomulka Nov 26, 2019
c36711e
fix config
pgomulka Nov 26, 2019
21ab922
fix config
pgomulka Nov 26, 2019
44e1af7
licences
pgomulka Nov 26, 2019
2f68c8e
licenses
pgomulka Nov 27, 2019
fe3b3ec
Merge branch 'master' into feature/ecs-log-lib
pgomulka Dec 19, 2019
bf44db6
prefix and stacktrace fix
pgomulka Dec 19, 2019
428bdda
Merge branch 'master' into feature/ecs-log-lib
pgomulka Dec 20, 2019
f483919
thrid party audit fix
pgomulka Dec 20, 2019
2b2c855
checkstyle
pgomulka Dec 20, 2019
ad58704
fixing customLoggin config pattern - system property cannot be looked up
pgomulka Dec 30, 2019
1f8360b
docker config to allign with regular distro
pgomulka Dec 30, 2019
cc2375b
Merge branch 'master' into feature/ecs-log-lib
pgomulka Dec 30, 2019
21ade62
change assertion on component not having abbreviated package
pgomulka Dec 30, 2019
ee58c82
class names are no longer abbreviated in json logs
pgomulka Dec 31, 2019
b63885f
minor cleanup
pgomulka Dec 31, 2019
5b2bb91
override asJson to allow calling this from org.elasticsearch.common.l…
pgomulka Dec 31, 2019
89bdfc5
javadoc
pgomulka Dec 31, 2019
8254701
rename es prefixed variables
pgomulka Dec 31, 2019
f9c1c0c
remove stacktrace as array
pgomulka Jan 7, 2020
7c4b43a
adjust tests to single line stactrace
pgomulka Jan 8, 2020
564ae23
Merge branch 'master' into feature/ecs-log-lib
pgomulka Jan 8, 2020
fbe7f53
unused import
pgomulka Jan 8, 2020
56816ff
checkstyle and debug info
pgomulka Jan 9, 2020
a4cbda8
sout to log
pgomulka Jan 9, 2020
5bc1aa9
fix the test to assert about single line stacktrace
pgomulka Jan 9, 2020
e9d176e
line length
pgomulka Jan 9, 2020
a0f5643
Merge branch 'master' into feature/ecs-log-lib
pgomulka Jan 9, 2020
9da7567
fix slow log message
pgomulka Jan 10, 2020
2d2ba00
Merge branch 'master' into feature/ecs-log-lib
pgomulka Jan 20, 2020
e9a0de2
remove duplication from config files by providing a wrapping layout
pgomulka Jan 20, 2020
9ab6747
imports and javadoc
pgomulka Jan 21, 2020
354d7e1
fix slow log tests
pgomulka Jan 21, 2020
969ad63
fix assertion
pgomulka Jan 22, 2020
14cfc28
Merge branch 'master' into feature/ecs-log-lib
pgomulka Jan 22, 2020
c41d51f
remove trailing spaces in docker config
pgomulka Jan 23, 2020
d1f2371
Merge branch 'master' into feature/ecs-log-lib
pgomulka Feb 14, 2020
56d12f3
Merge branch 'master' into feature/ecs-log-lib
elasticmachine Feb 26, 2020
134a0f7
bring back deprecated nodeAndClusterId
pgomulka Mar 18, 2020
bee1f15
Merge branch 'master' into feature/ecs-log-lib
pgomulka Mar 19, 2020
e84f7b8
support both ECSJsonlayout and ESJsonLayout
pgomulka Mar 19, 2020
b71f41a
revert a file modified by mistake
pgomulka Mar 19, 2020
e16c84e
fix test
pgomulka Mar 19, 2020
f18c235
remove plaintext slow logs
pgomulka Mar 19, 2020
88c1a75
Merge branch 'master' into feature/ecs-log-lib
pgomulka Mar 20, 2020
bde0558
test previous ES config
pgomulka Mar 20, 2020
3dce390
license header
pgomulka Mar 20, 2020
8f8ed5c
Merge branch 'master' into feature/ecs-log-lib
pgomulka Mar 27, 2020
4d055a2
Update qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTes…
pgomulka Mar 27, 2020
3037657
code review follow up
pgomulka Mar 27, 2020
5b4272b
Merge branch 'feature/ecs-log-lib' of github.com:pgomulka/elasticsear…
pgomulka Mar 27, 2020
99f8ab2
remove todo
pgomulka Mar 27, 2020
7b80a28
Merge branch 'master' into feature/ecs-log-lib
pgomulka Mar 30, 2020
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 buildSrc/version.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ supercsv = 2.4.0
# when updating log4j, please update also docs/java-api/index.asciidoc
log4j = 2.11.1
slf4j = 1.6.2
ecsLogging = 0.1.3

# when updating the JNA version, also update the version in buildSrc/build.gradle
jna = 4.5.1
Expand Down
10 changes: 4 additions & 6 deletions distribution/docker/src/docker/config/log4j2.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ logger.action.level = debug

appender.rolling.type = Console
appender.rolling.name = rolling
appender.rolling.layout.type = ESJsonLayout
appender.rolling.layout.type = ECSJsonLayout
appender.rolling.layout.type_name = server

rootLogger.level = info
rootLogger.appenderRef.rolling.ref = rolling

appender.deprecation_rolling.type = Console
appender.deprecation_rolling.name = deprecation_rolling
appender.deprecation_rolling.layout.type = ESJsonLayout
appender.deprecation_rolling.layout.type = ECSJsonLayout
appender.deprecation_rolling.layout.type_name = deprecation

logger.deprecation.name = org.elasticsearch.deprecation
Expand All @@ -24,9 +24,8 @@ logger.deprecation.additivity = false

appender.index_search_slowlog_rolling.type = Console
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.layout.type = ESJsonLayout
appender.index_search_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_search_slowlog_rolling.layout.type_name = index_search_slowlog
appender.index_search_slowlog_rolling.layout.overrideFields=message

logger.index_search_slowlog_rolling.name = index.search.slowlog
logger.index_search_slowlog_rolling.level = trace
Expand All @@ -35,9 +34,8 @@ logger.index_search_slowlog_rolling.additivity = false

appender.index_indexing_slowlog_rolling.type = Console
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.layout.type = ESJsonLayout
appender.index_indexing_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_indexing_slowlog_rolling.layout.type_name = index_indexing_slowlog
appender.index_indexing_slowlog_rolling.layout.overrideFields=message

logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = trace
Expand Down
8 changes: 4 additions & 4 deletions distribution/docker/src/docker/config/oss/log4j2.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ logger.action.level = debug

appender.rolling.type = Console
appender.rolling.name = rolling
appender.rolling.layout.type = ESJsonLayout
appender.rolling.layout.type = ECSJsonLayout
appender.rolling.layout.type_name = server

rootLogger.level = info
rootLogger.appenderRef.rolling.ref = rolling

appender.deprecation_rolling.type = Console
appender.deprecation_rolling.name = deprecation_rolling
appender.deprecation_rolling.layout.type = ESJsonLayout
appender.deprecation_rolling.layout.type = ECSJsonLayout
appender.deprecation_rolling.layout.type_name = deprecation

logger.deprecation.name = org.elasticsearch.deprecation
Expand All @@ -24,7 +24,7 @@ logger.deprecation.additivity = false

appender.index_search_slowlog_rolling.type = Console
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.layout.type = ESJsonLayout
appender.index_search_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_search_slowlog_rolling.layout.type_name = index_search_slowlog

logger.index_search_slowlog_rolling.name = index.search.slowlog
Expand All @@ -34,7 +34,7 @@ logger.index_search_slowlog_rolling.additivity = false

appender.index_indexing_slowlog_rolling.type = Console
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.layout.type = ESJsonLayout
appender.index_indexing_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_indexing_slowlog_rolling.layout.type_name = index_indexing_slowlog

logger.index_indexing_slowlog.name = index.indexing.slowlog.index
Expand Down
63 changes: 8 additions & 55 deletions distribution/src/config/log4j2.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%ma
appender.rolling.type = RollingFile
appender.rolling.name = rolling
appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_server.json
appender.rolling.layout.type = ESJsonLayout
appender.rolling.layout.type = ECSJsonLayout
appender.rolling.layout.type_name = server

appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}-%i.json.gz
Expand Down Expand Up @@ -61,7 +61,7 @@ rootLogger.appenderRef.rolling_old.ref = rolling_old
appender.deprecation_rolling.type = RollingFile
appender.deprecation_rolling.name = deprecation_rolling
appender.deprecation_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.json
appender.deprecation_rolling.layout.type = ESJsonLayout
appender.deprecation_rolling.layout.type = ECSJsonLayout
appender.deprecation_rolling.layout.type_name = deprecation

appender.deprecation_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation-%i.json.gz
Expand All @@ -71,35 +71,19 @@ appender.deprecation_rolling.policies.size.size = 1GB
appender.deprecation_rolling.strategy.type = DefaultRolloverStrategy
appender.deprecation_rolling.strategy.max = 4
#################################################
######## Deprecation - old style pattern #######
appender.deprecation_rolling_old.type = RollingFile
appender.deprecation_rolling_old.name = deprecation_rolling_old
appender.deprecation_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.log
appender.deprecation_rolling_old.layout.type = PatternLayout
appender.deprecation_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n

appender.deprecation_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_deprecation-%i.log.gz
appender.deprecation_rolling_old.policies.type = Policies
appender.deprecation_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
appender.deprecation_rolling_old.policies.size.size = 1GB
appender.deprecation_rolling_old.strategy.type = DefaultRolloverStrategy
appender.deprecation_rolling_old.strategy.max = 4
#################################################

logger.deprecation.name = org.elasticsearch.deprecation
logger.deprecation.level = warn
logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling
logger.deprecation.appenderRef.deprecation_rolling_old.ref = deprecation_rolling_old
logger.deprecation.additivity = false

######## Search slowlog JSON ####################
appender.index_search_slowlog_rolling.type = RollingFile
appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
appender.index_search_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs\
.cluster_name}_index_search_slowlog.json
appender.index_search_slowlog_rolling.layout.type = ESJsonLayout
appender.index_search_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_search_slowlog_rolling.layout.type_name = index_search_slowlog
appender.index_search_slowlog_rolling.layout.overrideFields=message

appender.index_search_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs\
.cluster_name}_index_search_slowlog-%i.json.gz
Expand All @@ -109,36 +93,21 @@ appender.index_search_slowlog_rolling.policies.size.size = 1GB
appender.index_search_slowlog_rolling.strategy.type = DefaultRolloverStrategy
appender.index_search_slowlog_rolling.strategy.max = 4
#################################################
######## Search slowlog - old style pattern ####
appender.index_search_slowlog_rolling_old.type = RollingFile
appender.index_search_slowlog_rolling_old.name = index_search_slowlog_rolling_old
appender.index_search_slowlog_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_search_slowlog.log
appender.index_search_slowlog_rolling_old.layout.type = PatternLayout
appender.index_search_slowlog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n

appender.index_search_slowlog_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_search_slowlog-%i.log.gz
appender.index_search_slowlog_rolling_old.policies.type = Policies
appender.index_search_slowlog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
appender.index_search_slowlog_rolling_old.policies.size.size = 1GB
appender.index_search_slowlog_rolling_old.strategy.type = DefaultRolloverStrategy
appender.index_search_slowlog_rolling_old.strategy.max = 4

#################################################
logger.index_search_slowlog_rolling.name = index.search.slowlog
logger.index_search_slowlog_rolling.level = trace
logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling_old.ref = index_search_slowlog_rolling_old
logger.index_search_slowlog_rolling.additivity = false

######## Indexing slowlog JSON ##################
appender.index_indexing_slowlog_rolling.type = RollingFile
appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
appender.index_indexing_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_indexing_slowlog.json
appender.index_indexing_slowlog_rolling.layout.type = ESJsonLayout
appender.index_indexing_slowlog_rolling.layout.type = ECSJsonLayout
appender.index_indexing_slowlog_rolling.layout.type_name = index_indexing_slowlog
appender.index_indexing_slowlog_rolling.layout.overrideFields=message


appender.index_indexing_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_indexing_slowlog-%i.json.gz
Expand All @@ -148,25 +117,9 @@ appender.index_indexing_slowlog_rolling.policies.size.size = 1GB
appender.index_indexing_slowlog_rolling.strategy.type = DefaultRolloverStrategy
appender.index_indexing_slowlog_rolling.strategy.max = 4
#################################################
######## Indexing slowlog - old style pattern ##
appender.index_indexing_slowlog_rolling_old.type = RollingFile
appender.index_indexing_slowlog_rolling_old.name = index_indexing_slowlog_rolling_old
appender.index_indexing_slowlog_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_indexing_slowlog.log
appender.index_indexing_slowlog_rolling_old.layout.type = PatternLayout
appender.index_indexing_slowlog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n

appender.index_indexing_slowlog_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
_index_indexing_slowlog-%i.log.gz
appender.index_indexing_slowlog_rolling_old.policies.type = Policies
appender.index_indexing_slowlog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
appender.index_indexing_slowlog_rolling_old.policies.size.size = 1GB
appender.index_indexing_slowlog_rolling_old.strategy.type = DefaultRolloverStrategy
appender.index_indexing_slowlog_rolling_old.strategy.max = 4
#################################################


logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = trace
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling_old.ref = index_indexing_slowlog_rolling_old
logger.index_indexing_slowlog.additivity = false
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ public DieWithDignityPlugin() {

@Override
public List<RestHandler> getRestHandlers(
final Settings settings,
final RestController restController,
final ClusterSettings clusterSettings,
final IndexScopedSettings indexScopedSettings,
final SettingsFilter settingsFilter,
final IndexNameExpressionResolver indexNameExpressionResolver,
final Supplier<DiscoveryNodes> nodesInCluster) {
final Settings settings,
final RestController restController,
final ClusterSettings clusterSettings,
final IndexScopedSettings indexScopedSettings,
final SettingsFilter settingsFilter,
final IndexNameExpressionResolver indexNameExpressionResolver,
final Supplier<DiscoveryNodes> nodesInCluster
) {
return Collections.singletonList(new RestDieWithDignityAction());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,14 @@
public class DieWithDignityIT extends ESRestTestCase {

public void testDieWithDignity() throws Exception {
expectThrows(
IOException.class,
() -> client().performRequest(new Request("GET", "/_die_with_dignity"))
);
expectThrows(IOException.class, () -> client().performRequest(new Request("GET", "/_die_with_dignity")));

// the Elasticsearch process should die and disappear from the output of jps
assertBusy(() -> {
final String jpsPath = PathUtils.get(System.getProperty("runtime.java.home"), "bin/jps").toString();
final Process process = new ProcessBuilder().command(jpsPath, "-v").start();

try (InputStream is = process.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
try (InputStream is = process.getInputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
String line;
while ((line = in.readLine()) != null) {
assertThat(line, line, not(containsString("-Ddie.with.dignity.test")));
Expand All @@ -60,21 +56,24 @@ public void testDieWithDignity() throws Exception {

// parse the logs and ensure that Elasticsearch died with the expected cause
final List<String> lines = Files.readAllLines(PathUtils.get(System.getProperty("log")));

final Iterator<String> it = lines.iterator();

boolean fatalError = false;
boolean fatalErrorInThreadExiting = false;
try {
while (it.hasNext() && (fatalError == false || fatalErrorInThreadExiting == false)) {
final String line = it.next();
if (line.matches(".*ERROR.*o\\.e\\.ExceptionsHelper.*integTest-0.*fatal error.*")) {
if (containsAll(line, ".*ERROR.*", ".*ExceptionsHelper.*", ".*integTest-0.*", ".*fatal error.*")) {
fatalError = true;
} else if (line.matches(".*ERROR.*o\\.e\\.b\\.ElasticsearchUncaughtExceptionHandler.*integTest-0.*"
+ "fatal error in thread \\[Thread-\\d+\\], exiting.*")) {
} else if (containsAll(
line,
".*ERROR.*",
".*ElasticsearchUncaughtExceptionHandler.*",
".*integTest-0.*",
".*fatal error in thread \\[Thread-\\d+\\], exiting.*",
".*java.lang.OutOfMemoryError: die with dignity.*"
)) {
fatalErrorInThreadExiting = true;
assertTrue(it.hasNext());
assertThat(it.next(), containsString("java.lang.OutOfMemoryError: die with dignity"));
}
}

Expand All @@ -88,6 +87,15 @@ public void testDieWithDignity() throws Exception {
}
}

private boolean containsAll(String line, String... subStrings) {
for (String subString : subStrings) {
if (line.matches(subString) == false) {
return false;
}
}
return true;
}

private void debugLogs(Path path) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(path)) {
reader.lines().forEach(line -> logger.info(line));
Expand All @@ -102,7 +110,8 @@ protected boolean preserveClusterUponCompletion() {

@Override
protected final Settings restClientSettings() {
return Settings.builder().put(super.restClientSettings())
return Settings.builder()
.put(super.restClientSettings())
// increase the timeout here to 90 seconds to handle long waits for a green
// cluster health. the waits for green need to be longer than a minute to
// account for delayed shards
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,14 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException,
"_deprecation.log";
final List<String> deprecationEvents = Files.readAllLines(PathUtils.get(deprecationPath));
// we appended an integer to each log message, use that for sorting
deprecationEvents.sort(Comparator.comparingInt(s -> Integer.parseInt(s.split("message")[1])));
Pattern pattern = Pattern.compile(".*message(\\d+)\"");
deprecationEvents.sort(Comparator.comparingInt(s -> {
Matcher matcher = pattern.matcher(s);
matcher.matches();
return Integer.parseInt(matcher.group(1));
}));
assertThat(deprecationEvents.size(), equalTo(128));

for (int i = 0; i < 128; i++) {
assertLogLine(
deprecationEvents.get(i),
Expand Down
5 changes: 4 additions & 1 deletion qa/logging-config/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ testClusters.integTest {

integTest.runner {
nonInputProperties.systemProperty 'tests.logfile',
"${-> testClusters.integTest.singleNode().getServerLog().absolutePath.replaceAll(".json", ".log")}"
"${-> testClusters.integTest.singleNode().getServerLog().absolutePath.replaceAll("_server.json", ".log")}"

nonInputProperties.systemProperty 'tests.jsonLogfile',
"${-> testClusters.integTest.singleNode().getServerLog()}"
}

test {
Expand Down
Loading