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

future-architect / uroborosql / #770

12 Sep 2024 03:28PM UTC coverage: 90.35% (-0.9%) from 91.21%
#770

push

web-flow
 Enable per-SQL-ID log suppression (#322) (#332)

* 各Logger用のインタフェースを作成し必要なクラスがimplementsするように修正。そのうえでSLF4J 2.xのAPIを利用するように変更(性能改善)

* Changed to use slf4j v2.0 API
Also added suppressLogging API

* Refactoring log-related class and method names.

384 of 587 new or added lines in 32 files covered. (65.42%)

9 existing lines in 7 files now uncovered.

8885 of 9834 relevant lines covered (90.35%)

0.9 hits per line

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

97.59
/src/main/java/jp/co/future/uroborosql/event/subscriber/AuditLogEventSubscriber.java
1
/**
2
 * Copyright (c) 2017-present, Future Corporation
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
package jp.co.future.uroborosql.event.subscriber;
8

9
import java.sql.ResultSet;
10
import java.sql.SQLException;
11

12
import org.slf4j.Logger;
13

14
import jp.co.future.uroborosql.context.ExecutionContext;
15
import jp.co.future.uroborosql.event.AfterSqlBatchEvent;
16
import jp.co.future.uroborosql.event.AfterSqlQueryEvent;
17
import jp.co.future.uroborosql.event.AfterSqlUpdateEvent;
18
import jp.co.future.uroborosql.log.support.EventLoggingSupport;
19

20
/**
21
 * 監査用ログを出力するイベントサブスクライバ
22
 *
23
 * @author H.Sugimoto
24
 * @since v1.0.0
25
 *
26
 */
27
public class AuditLogEventSubscriber extends EventSubscriber implements EventLoggingSupport {
28
        /** イベントロガー */
29
        private static final Logger EVENT_LOG = EventLoggingSupport.getEventLogger("auditlog");
1✔
30

31
        /** 機能名取得用のパラメータキー名 */
32
        private String funcIdKey = "_funcId";
1✔
33

34
        /** ユーザ名取得用のパラメータキー名 */
35
        private String userNameKey = "_userName";
1✔
36

37
        /** ユーザ名の初期値 */
38
        private static final String DEFAULT_USER_NAME = "UNKNOWN";
39

40
        /** 機能名の初期値 */
41
        private static final String DEFAULT_FUNC_ID = "UNKNOWN";
42

43
        /**
44
         * コンストラクタ
45
         */
46
        public AuditLogEventSubscriber() {
1✔
47
        }
1✔
48

49
        /**
50
         *
51
         * {@inheritDoc}
52
         *
53
         * @see jp.co.future.uroborosql.event.subscriber.EventSubscriber#initialize()
54
         */
55
        @Override
56
        public void initialize() {
57
                afterSqlQueryListener(this::afterSqlQuery);
1✔
58
                afterSqlUpdateListener(this::afterSqlUpdate);
1✔
59
                afterSqlBatchListener(this::afterSqlBatch);
1✔
60
        }
1✔
61

62
        void afterSqlQuery(final AfterSqlQueryEvent evt) {
63
                var resultSet = evt.getResultSet();
1✔
64
                // カウント初期値
65
                var rowCount = -1;
1✔
66
                try {
67
                        // resultSetのカーソル種別を取得
68
                        // 種別「TYPE_FORWARD_ONLY」の場合、beforeFirstメソッドが効かないため除外
69
                        if (resultSet.getType() != ResultSet.TYPE_FORWARD_ONLY) {
1✔
70
                                // 件数結果取得
71
                                resultSet.last();
1✔
72
                                rowCount = resultSet.getRow();
1✔
73
                                resultSet.beforeFirst();
1✔
74
                        }
75
                } catch (SQLException ex) {
×
76
                        // ここでの例外は実処理に影響を及ぼさないよう握りつぶす
77
                }
1✔
78

79
                var userName = getParam(evt.getExecutionContext(), userNameKey, DEFAULT_USER_NAME);
1✔
80
                var funcId = getParam(evt.getExecutionContext(), funcIdKey, DEFAULT_FUNC_ID);
1✔
81
                var reportRowCount = rowCount;
1✔
82
                debugWith(EVENT_LOG)
1✔
83
                                .setMessage("AuditData: {}")
1✔
84
                                .addArgument(() -> new AuditData(userName,
1✔
85
                                                funcId,
86
                                                evt.getExecutionContext().getSqlId(),
1✔
87
                                                evt.getExecutionContext().getSqlName(),
1✔
88
                                                evt.getExecutionContext().getExecutableSql(),
1✔
89
                                                reportRowCount))
90
                                .log();
1✔
91
        }
1✔
92

93
        void afterSqlUpdate(final AfterSqlUpdateEvent evt) {
94
                var userName = getParam(evt.getExecutionContext(), userNameKey, DEFAULT_USER_NAME);
1✔
95
                var funcId = getParam(evt.getExecutionContext(), funcIdKey, DEFAULT_FUNC_ID);
1✔
96
                debugWith(EVENT_LOG)
1✔
97
                                .setMessage("AuditData: {}")
1✔
98
                                .addArgument(() -> new AuditData(userName,
1✔
99
                                                funcId,
100
                                                evt.getExecutionContext().getSqlId(),
1✔
101
                                                evt.getExecutionContext().getSqlName(),
1✔
102
                                                evt.getExecutionContext().getExecutableSql(),
1✔
103
                                                evt.getCount()))
1✔
104
                                .log();
1✔
105
        }
1✔
106

107
        void afterSqlBatch(final AfterSqlBatchEvent evt) {
108
                var userName = getParam(evt.getExecutionContext(), userNameKey, DEFAULT_USER_NAME);
1✔
109
                var funcId = getParam(evt.getExecutionContext(), funcIdKey, DEFAULT_FUNC_ID);
1✔
110
                debugWith(EVENT_LOG)
1✔
111
                                .setMessage("AuditData: {}")
1✔
112
                                .addArgument(() -> {
1✔
113
                                        var rowCount = -1;
1✔
114
                                        try {
115
                                                rowCount = evt.getPreparedStatement().getUpdateCount();
1✔
NEW
116
                                        } catch (SQLException ex) {
×
117
                                                // ここでの例外は実処理に影響を及ぼさないよう握りつぶす
118
                                        }
1✔
119
                                        return new AuditData(userName,
1✔
120
                                                        funcId,
121
                                                        evt.getExecutionContext().getSqlId(),
1✔
122
                                                        evt.getExecutionContext().getSqlName(),
1✔
123
                                                        evt.getExecutionContext().getExecutableSql(),
1✔
124
                                                        rowCount);
125
                                })
126
                                .log();
1✔
127
        }
1✔
128

129
        /**
130
         * バインドパラメータに設定した機能IDのキー名を設定する.
131
         *
132
         * @param funcIdKey 機能IDのキー名
133
         * @return AuditLogEventSubscriber
134
         */
135
        public AuditLogEventSubscriber setFuncIdKey(final String funcIdKey) {
136
                this.funcIdKey = funcIdKey;
1✔
137
                return this;
1✔
138
        }
139

140
        /**
141
         * バインドパラメータに設定したユーザ名のキー名を設定する.
142
         *
143
         * @param userNameKey ユーザ名のキー名
144
         * @return AuditLogEventSubscriber
145
         */
146
        public AuditLogEventSubscriber setUserNameKey(final String userNameKey) {
147
                this.userNameKey = userNameKey;
1✔
148
                return this;
1✔
149
        }
150

151
        /**
152
         * パラメータ値を取得する
153
         *
154
         * @param ctx ExecutionContext
155
         * @param key パラメータのキー
156
         * @param nullDefault パラメータ値が取得できなかった時のデフォルト値
157
         * @return パラメータの値。キーに対するパラメータが存在しない場合は<code>null</code>.
158
         */
159
        private String getParam(final ExecutionContext ctx, final String key, final String nullDefault) {
160
                var param = ctx.getParam(key);
1✔
161
                if (param == null) {
1✔
162
                        return nullDefault;
1✔
163
                } else {
164
                        return String.valueOf(param.getValue());
1✔
165
                }
166
        }
167

168
        /**
169
         * 監査用データ
170
         */
171
        private static final class AuditData {
172
                /** SQL文中でJSONとして不正な文字を置換するためのMap */
173
                private static final String[] ESC_CHARS = {
1✔
174
                                "\\\\:\\\\\\\\",
175
                                "\\\":\\\\\\\"",
176
                                "/:\\\\/",
177
                                "\\t:\\\\t",
178
                                "\\f:\\\\f",
179
                                "\\r\\n: ",
180
                                "\\n: ",
181
                                "\\r: "
182
                };
183

184
                private final String userName;
185
                private final String funcId;
186
                private final String sqlId;
187
                private final String sqlName;
188
                private final String sql;
189
                private final int rowCount;
190

191
                /**
192
                 * コンストラクタ
193
                 *
194
                 * @param userName ユーザ名
195
                 * @param funcId 機能ID
196
                 * @param sqlId SQL-ID
197
                 * @param sqlName SQL名
198
                 * @param sql SQL文
199
                 * @param rowCount 件数
200
                 */
201
                private AuditData(final String userName, final String funcId, final String sqlId, final String sqlName,
202
                                final String sql, final int rowCount) {
1✔
203
                        this.userName = userName;
1✔
204
                        this.funcId = funcId;
1✔
205
                        this.sqlId = sqlId;
1✔
206
                        this.sqlName = sqlName;
1✔
207
                        this.sql = sql;
1✔
208
                        this.rowCount = rowCount;
1✔
209
                }
1✔
210

211
                /**
212
                 * JSONとして不正な文字をエスケープする.
213
                 *
214
                 * @param str エスケープ対象文字列
215
                 * @return エスケープ後文字列
216
                 */
217
                private String escapeJson(final String str) {
218
                        if (str == null) {
1✔
219
                                return null;
1✔
220
                        }
221
                        var buff = str;
1✔
222
                        for (var escChar : ESC_CHARS) {
1✔
223
                                var parts = escChar.split(":");
1✔
224
                                buff = buff.replaceAll(parts[0], parts[1]);
1✔
225
                        }
226
                        return buff;
1✔
227
                }
228

229
                /**
230
                 * {@inheritDoc}
231
                 *
232
                 * @see java.lang.Object#toString()
233
                 */
234
                @Override
235
                public String toString() {
236
                        return "{\"userName\":\"" + escapeJson(userName)
1✔
237
                                        + "\",\"funcId\":\"" + escapeJson(funcId)
1✔
238
                                        + "\",\"sqlId\":\"" + escapeJson(sqlId)
1✔
239
                                        + "\",\"sqlName\":\"" + escapeJson(sqlName)
1✔
240
                                        + "\",\"sql\":\"" + escapeJson(sql)
1✔
241
                                        + "\",\"rowCount\":" + rowCount + "}";
242
                }
243

244
        }
245
}
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