• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 17208175973

25 Aug 2025 12:00PM UTC coverage: 63.742% (+0.07%) from 63.671%
17208175973

push

github

ibacher
TRUNK-6395: Saner scheme for copying properties from the installation script (#5260)

0 of 2 new or added lines in 1 file covered. (0.0%)

697 existing lines in 13 files now uncovered.

22147 of 34745 relevant lines covered (63.74%)

0.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.27
/api/src/main/java/org/openmrs/logging/OpenmrsConfigurationFactory.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.logging;
11

12
import org.apache.commons.io.FileUtils;
13
import org.apache.commons.io.FilenameUtils;
14
import org.apache.commons.lang3.StringUtils;
15
import org.apache.logging.log4j.Level;
16
import org.apache.logging.log4j.core.LoggerContext;
17
import org.apache.logging.log4j.core.config.AbstractConfiguration;
18
import org.apache.logging.log4j.core.config.Configuration;
19
import org.apache.logging.log4j.core.config.ConfigurationFactory;
20
import org.apache.logging.log4j.core.config.ConfigurationSource;
21
import org.apache.logging.log4j.core.config.LoggerConfig;
22
import org.apache.logging.log4j.core.config.Order;
23
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
24
import org.apache.logging.log4j.core.config.json.JsonConfiguration;
25
import org.apache.logging.log4j.core.config.plugins.Plugin;
26
import org.apache.logging.log4j.core.config.xml.XmlConfiguration;
27
import org.apache.logging.log4j.core.config.yaml.YamlConfiguration;
28
import org.openmrs.api.AdministrationService;
29
import org.openmrs.api.ServiceNotFoundException;
30
import org.openmrs.api.context.Context;
31
import org.openmrs.util.OpenmrsConstants;
32
import org.openmrs.util.OpenmrsUtil;
33
import org.slf4j.LoggerFactory;
34

35
import javax.validation.constraints.NotNull;
36
import java.io.File;
37
import java.net.URI;
38
import java.util.ArrayList;
39
import java.util.Arrays;
40
import java.util.Comparator;
41
import java.util.List;
42
import java.util.Locale;
43

44
/**
45
 * {@link ConfigurationFactory} to handle OpenMRS's logging configuration.
46
 * <p/>
47
 * Functionality provided by this {@link ConfigurationFactory}:
48
 * <ul>
49
 *     <li>Load log4j2 configuration files from the OpenMRS application directory</li>
50
 *     <li>Ensures that the configuration includes the MEMORY_APPENDER to keep log files in memory</li>
51
 *     <li>Allows the <tt>log.level</tt> setting to override logger settings</li>
52
 * </ul>
53
 */
54
@Plugin(name = "OpenmrsConfigurationFactory", category = ConfigurationFactory.CATEGORY)
55
@Order(10)
56
@SuppressWarnings("unused")
57
public class OpenmrsConfigurationFactory extends ConfigurationFactory {
1✔
58
        
59
        private static final org.slf4j.Logger log = LoggerFactory.getLogger(OpenmrsConfigurationFactory.class);
1✔
60
        
61
        public static final String[] SUFFIXES = new String[] { ".xml", ".yml", ".yaml", ".json", "*" };
1✔
62

63
        // Extensions are all SUFFIXES except wildcard, with leading "." removed
64
        public static final String[] EXTENSIONS = Arrays.stream(SUFFIXES)
1✔
65
                .filter(s -> !s.equals("*")).map(s -> s.substring(1)).toArray(String[]::new);
1✔
66

67
        @Override
68
        public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) {
69
                if (!isActive()) {
1✔
70
                        return null;
×
71
                }
72

73
                // try to load the configuration from the application data directory
74
                if (configLocation == null) {
1✔
75
                        List<File> configurationFiles = getConfigurationFiles();
1✔
76
                        if (!configurationFiles.isEmpty()) {
1✔
77
                                if (configurationFiles.size() == 1) {
×
78
                                        System.out.println("Adding log4j2 configuration file: " + configurationFiles.get(0).getPath());
×
79
                                        return super.getConfiguration(loggerContext, name, configurationFiles.get(0).toURI());
×
80
                                }
81
                                else {
82
                                        List<AbstractConfiguration> abstractConfigurations = new ArrayList<>();
×
83
                                        for (File configFile : configurationFiles) {
×
84
                                                Configuration configuration = super.getConfiguration(loggerContext, name, configFile.toURI());
×
85
                                                if (configuration instanceof AbstractConfiguration) {
×
86
                                                        System.out.println("Adding log4j2 configuration file: " + configFile.getPath());
×
87
                                                        abstractConfigurations.add((AbstractConfiguration) configuration);
×
88
                                                }
89
                                                else {
90
                                                        System.err.println("Unable to add log4j2 configuration file: " + configFile.getPath());
×
91
                                                }
92
                                        }
×
93
                                        return new CompositeConfiguration(abstractConfigurations);
×
94
                                }
95
                        }
96
                }
97

98
                return super.getConfiguration(loggerContext, name, configLocation);
1✔
99
        }
100
        
101
        @Override
102
        public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
103
                if (source != null && source.getLocation() != null) {
1✔
104
                        switch (FilenameUtils.getExtension(source.getLocation()).toLowerCase(Locale.ROOT)) {
1✔
105
                                case "xml":
106
                                        return new OpenmrsXmlConfiguration(loggerContext, source);
1✔
107
                                case "yaml":
108
                                case "yml":
UNCOV
109
                                        return new OpenmrsYamlConfiguration(loggerContext, source);
×
110
                                case "json":
UNCOV
111
                                        return new OpenmrsJsonConfiguration(loggerContext, source);
×
112
                                default:
113
                                        throw new IllegalArgumentException(
×
UNCOV
114
                                                OpenmrsConfigurationFactory.class.getName() + " does not know how to handle source " + source.getFile());
×
115
                        }
116
                }
UNCOV
117
                return null;
×
118
        }
119
        
120
        @Override
121
        protected String[] getSupportedTypes() {
122
                return SUFFIXES;
1✔
123
        }
124
        
125
        public List<File> getConfigurationFiles() {
126
                List<File> configurationFiles = new ArrayList<>();
1✔
127
                for (File configDir : new File[] {
1✔
128
                        OpenmrsUtil.getDirectoryInApplicationDataDirectory("configuration"),
1✔
129
                        OpenmrsUtil.getApplicationDataDirectoryAsFile()
1✔
130
                }) {
131
                        for (File configFile : FileUtils.listFiles(configDir, EXTENSIONS, false)) {
1✔
132
                                if (configFile.getName().startsWith(getDefaultPrefix()) && configFile.canRead()) {
×
UNCOV
133
                                        configurationFiles.add(configFile);
×
134
                                }
UNCOV
135
                        }
×
136
                }
137
                configurationFiles.sort(Comparator.comparing(File::getName));
1✔
138
                return configurationFiles;
1✔
139
        }
140
        
141
        protected static void doOpenmrsCustomisations(AbstractConfiguration configuration) {
142
                // if we don't have an in-memory appender, add it
143
                MemoryAppender memoryAppender = configuration.getAppender(OpenmrsConstants.MEMORY_APPENDER_NAME);
1✔
144
                if (memoryAppender == null) {
1✔
145
                        memoryAppender = MemoryAppender.newBuilder().build();
1✔
146
                        memoryAppender.start();
1✔
147
                        
148
                        configuration.addAppender(memoryAppender);
1✔
149
                }
150
                
151
                LoggerConfig rootLogger = configuration.getRootLogger();
1✔
152
                if (rootLogger.getAppenders().get(OpenmrsConstants.MEMORY_APPENDER_NAME) == null) {
1✔
153
                        rootLogger.addAppender(memoryAppender, null, memoryAppender.getFilter());
1✔
154
                }
155
                
156
                try {
UNCOV
157
                        AdministrationService adminService = Context.getAdministrationService();
×
UNCOV
158
                        applyLogLevels(configuration, adminService);
×
159
                } catch (ServiceNotFoundException e) {
1✔
160
                        // if AdministrativeService is not available, we'll assume we're starting up and everything is ok
161
                        if (!e.getServiceClass().isAssignableFrom(AdministrationService.class)) {
1✔
UNCOV
162
                                throw e;
×
163
                        }
UNCOV
164
                }
×
165
        }
1✔
166
        
167
        private static void applyLogLevels(AbstractConfiguration configuration, AdministrationService adminService) {
168
                String logLevel = adminService.getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_LOG_LEVEL, "");
×
169
                
170
                for (String level : logLevel.split(",")) {
×
171
                        String[] classAndLevel = level.split(":");
×
172
                        if (classAndLevel.length == 0) {
×
UNCOV
173
                                break;
×
174
                        } else if (classAndLevel.length == 1) {
×
UNCOV
175
                                applyLogLevel(configuration, OpenmrsConstants.LOG_CLASS_DEFAULT, classAndLevel[0].trim());
×
176
                        } else {
177
                                applyLogLevel(configuration, classAndLevel[0].trim(), classAndLevel[1].trim());
×
178
                        }
179
                }
180
        }
×
181
        
182
        private static void applyLogLevel(AbstractConfiguration configuration, @NotNull String loggerName, String loggerLevel) {
UNCOV
183
                if (StringUtils.isBlank(loggerLevel)) {
×
184
                        return;
×
185
                }
186
                
UNCOV
187
                if (loggerName == null) {
×
188
                        loggerName = OpenmrsConstants.LOG_CLASS_DEFAULT;
×
189
                }
190
                
UNCOV
191
                LoggerConfig loggerConfig = configuration.getLogger(loggerName);
×
192
                if (loggerConfig != null) {
×
193
                        switch (loggerLevel.toLowerCase(Locale.ROOT)) {
×
194
                                case OpenmrsConstants.LOG_LEVEL_TRACE:
195
                                        loggerConfig.setLevel(Level.TRACE);
×
196
                                        break;
×
197
                                case OpenmrsConstants.LOG_LEVEL_DEBUG:
198
                                        loggerConfig.setLevel(Level.DEBUG);
×
199
                                        break;
×
200
                                case OpenmrsConstants.LOG_LEVEL_INFO:
201
                                        loggerConfig.setLevel(Level.INFO);
×
202
                                        break;
×
203
                                case OpenmrsConstants.LOG_LEVEL_WARN:
204
                                        loggerConfig.setLevel(Level.WARN);
×
205
                                        break;
×
206
                                case OpenmrsConstants.LOG_LEVEL_ERROR:
207
                                        loggerConfig.setLevel(Level.ERROR);
×
208
                                        break;
×
209
                                case OpenmrsConstants.LOG_LEVEL_FATAL:
210
                                        loggerConfig.setLevel(Level.FATAL);
×
UNCOV
211
                                        break;
×
212
                                default:
UNCOV
213
                                        log.warn("Log level {} is invalid. " +
×
214
                                                "Valid values are trace, debug, info, warn, error or fatal", loggerLevel);
215
                                        break;
216
                        }
217
                }
UNCOV
218
        }
×
219
        
220
        private static class OpenmrsXmlConfiguration extends XmlConfiguration {
221
                
222
                public OpenmrsXmlConfiguration(LoggerContext loggerContext, ConfigurationSource configSource) {
223
                        super(loggerContext, configSource);
1✔
224
                }
1✔
225
                
226
                @Override
227
                protected void doConfigure() {
228
                        super.doConfigure();
1✔
229
                        doOpenmrsCustomisations(this);
1✔
230
                }
1✔
231
        }
232
        
233
        private static class OpenmrsYamlConfiguration extends YamlConfiguration {
234
                
235
                public OpenmrsYamlConfiguration(LoggerContext loggerContext, ConfigurationSource configSource) {
UNCOV
236
                        super(loggerContext, configSource);
×
UNCOV
237
                }
×
238
                
239
                @Override
240
                protected void doConfigure() {
UNCOV
241
                        super.doConfigure();
×
UNCOV
242
                        doOpenmrsCustomisations(this);
×
UNCOV
243
                }
×
244
        }
245
        
246
        private static class OpenmrsJsonConfiguration extends JsonConfiguration {
247
                
248
                public OpenmrsJsonConfiguration(LoggerContext loggerContext, ConfigurationSource configSource) {
UNCOV
249
                        super(loggerContext, configSource);
×
UNCOV
250
                }
×
251
                
252
                @Override
253
                protected void doConfigure() {
UNCOV
254
                        super.doConfigure();
×
UNCOV
255
                        doOpenmrsCustomisations(this);
×
UNCOV
256
                }
×
257
        }
258
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc