Skip to content

Commit 558a8ee

Browse files
authored
Merge pull request #216 from DevKor-github/feat/logging
Feat: 사용자 행동 로깅 추가
2 parents adc2f76 + 9749f72 commit 558a8ee

5 files changed

Lines changed: 104 additions & 4 deletions

File tree

build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ dependencies {
120120

121121
// thumbnail
122122
implementation 'net.coobird:thumbnailator:0.4.19'
123+
124+
// elasticsearch
125+
// implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
123126
}
124127

125128
dependencyManagement {

src/main/java/devkor/com/teamcback/domain/routes/service/RouteService.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import devkor.com.teamcback.domain.routes.repository.NodeRepository;
1515
import devkor.com.teamcback.global.exception.exception.AdminException;
1616
import devkor.com.teamcback.global.exception.exception.GlobalException;
17+
import devkor.com.teamcback.global.logging.service.LogUtil;
1718
import lombok.RequiredArgsConstructor;
1819
import lombok.extern.slf4j.Slf4j;
1920
import org.springframework.stereotype.Service;
@@ -33,6 +34,8 @@ public class RouteService {
3334
private final PlaceRepository placeRepository;
3435
private final CheckpointRepository checkpointRepository;
3536
private final ConnectedBuildingRepository connectedBuildingRepository;
37+
private final LogUtil logUtil;
38+
3639
private static final long OUTDOOR_ID = 0L;
3740
private static final String SEPARATOR = ",";
3841
private static final Long INF = Long.MAX_VALUE;
@@ -76,9 +79,26 @@ public List<GetRouteRes> findRoute(LocationType startType, Long startId, Double
7679
// if(route.getPath().isEmpty() && route2.getPath.isEmpty()) throw new GlobalException(NOT_FOUND_ROUTE);
7780
// routeRes.add(buildRouteResponse(route2, isStartBuilding, isEndBuilding));
7881

82+
// 로그 저장
83+
Building startBuilding = null, endBuilding = null;
84+
Place startPlace= null, endPlace = null;
85+
86+
if(checkType(startType, endType)) {
87+
if (startType == LocationType.BUILDING) startBuilding = findBuilding(startId);
88+
else if (startType == LocationType.PLACE) startPlace = findPlace(startId);
89+
90+
if (endType == LocationType.BUILDING) endBuilding = findBuilding(endId);
91+
else if (endType == LocationType.PLACE) endPlace = findPlace(endId);
92+
93+
logUtil.logRoute(startBuilding, startPlace, endBuilding, endPlace, conditions);
94+
}
7995
return routeRes;
8096
}
8197

98+
private boolean checkType(LocationType startType, LocationType endType) {
99+
return (startType == LocationType.BUILDING || startType == LocationType.PLACE) && (endType == LocationType.BUILDING || endType == LocationType.PLACE);
100+
}
101+
82102
/**
83103
* 에러 조건 확인
84104
*/
@@ -570,4 +590,4 @@ public int compareTo(NodeDistancePair other) {
570590
}
571591
}
572592

573-
}
593+
}

src/main/java/devkor/com/teamcback/domain/search/service/SearchService.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import devkor.com.teamcback.domain.user.entity.User;
2424
import devkor.com.teamcback.domain.user.repository.UserRepository;
2525
import devkor.com.teamcback.global.exception.exception.GlobalException;
26+
import devkor.com.teamcback.global.logging.service.LogUtil;
2627
import devkor.com.teamcback.global.response.CursorPageRes;
2728
import lombok.RequiredArgsConstructor;
2829
import lombok.extern.slf4j.Slf4j;
@@ -55,6 +56,7 @@ public class SearchService {
5556
private final BookmarkRepository bookmarkRepository;
5657
private final PlaceImageRepository placeImageRepository;
5758
private final NodeRepository nodeRepository;
59+
private final LogUtil logUtil;
5860
private final FileUtil fileUtil;
5961

6062
// 점수 계산을 위한 상수
@@ -73,10 +75,11 @@ public class SearchService {
7375

7476
// 통합 검색 결과에 표시하지 않을 편의시설 종류
7577
//TODO: 자전거보관소, 벤치 디자인 요청 후 List에서 제거
78+
// TODO: 리필로드 구현 완료되면 REUSABLE_CUP_RETURN 제거하기
7679
private static final List<String> excludedTypes = Arrays.asList(PlaceType.CLASSROOM.getName(), PlaceType.TOILET.getName(),
7780
PlaceType.MEN_TOILET.getName(), PlaceType.WOMEN_TOILET.getName(), PlaceType.MEN_HANDICAPPED_TOILET.getName(),
7881
PlaceType.WOMEN_HANDICAPPED_TOILET.getName(), PlaceType.LOCKER.getName(), PlaceType.TRASH_CAN.getName(),
79-
PlaceType.BICYCLE_RACK.getName(), PlaceType.BENCH.getName());
82+
PlaceType.BICYCLE_RACK.getName(), PlaceType.BENCH.getName(), PlaceType.REUSABLE_CUP_RETURN.getName());
8083

8184
// 통합 검색 결과에서 "건물명 + 기본편의시설명"의 형태로 제공되어야 하는 편의시설 종류
8285
private static final List<PlaceType> outerTagTypes = Arrays.asList(PlaceType.CAFE, PlaceType.CAFETERIA, PlaceType.CONVENIENCE_STORE,
@@ -134,6 +137,8 @@ public GlobalSearchListRes globalSearch(String word, Long userId) {
134137
scores.putAll(getScores(word, buildings, lastPlaceWord, lastBuildingWord, user));
135138
}
136139
}
140+
141+
logUtil.logSearch(word);
137142
return new GlobalSearchListRes(orderSequence(scores));
138143
}
139144

@@ -155,6 +160,7 @@ public SearchBuildingListRes searchBuildings(PlaceType type) {
155160
.map(Place::getBuilding)
156161
.distinct()
157162
.toList();
163+
logUtil.logClick(null, null, null, type.toString());
158164
}
159165

160166
List<SearchBuildingRes> resList = new ArrayList<>();
@@ -197,6 +203,7 @@ public SearchBuildingFacilityListRes searchBuildingFacilityByType(Long buildingI
197203

198204
res.setFacilities(map);
199205

206+
logUtil.logClick(building.getName(), null, null, placeType.toString());
200207
return res;
201208
}
202209

@@ -266,7 +273,7 @@ public SearchFacilityListRes searchFacilitiesWithType(PlaceType placeType) {
266273

267274
placeResList.add(new SearchPlaceRes(place, imageUrl));
268275
}
269-
276+
logUtil.logClick(null, null, null, placeType.toString());
270277
return new SearchFacilityListRes(placeResList);
271278
}
272279

@@ -280,6 +287,7 @@ public SearchPlaceByMaskIndexRes searchPlaceByMaskIndex(Long buildingId, int flo
280287
if(place == null) {
281288
throw new GlobalException(NOT_FOUND_PLACE);
282289
}
290+
logUtil.logClick(building.getName(), place.getName(), place.getFloor(), place.getType().toString());
283291
return new SearchPlaceByMaskIndexRes(place);
284292
}
285293

@@ -334,9 +342,13 @@ public SearchBuildingDetailRes searchBuildingDetail(Long userId, Long buildingId
334342
if(building.getFileUuid() != null) {
335343
imageUrl = fileUtil.getThumbnail(building.getFileUuid());
336344
}
345+
logUtil.logClick(building.getName(), null, null, null);
337346
return new SearchBuildingDetailRes(res, containPlaceTypes, building, imageUrl, bookmarked);
338347
}
339348

349+
/**
350+
* 건물 대표 편의시설 목록 조회
351+
*/
340352
@Transactional(readOnly = true)
341353
public CursorPageRes<SearchMainFacilityRes> searchBuildingMainFacilityList(Long buildingId, Long lastPlaceId, int size) {
342354
Place lastPlace = (lastPlaceId == null) ? null : findPlace(lastPlaceId);
@@ -393,7 +405,7 @@ public SearchPlaceDetailRes searchPlaceDetail(Long userId, Long placeId) {
393405
if(bookmarkRepository.existsByLocationIdAndLocationTypeAndCategoryBookmarkList_CategoryIn(place.getId(), LocationType.PLACE, categories)) {
394406
bookmarked = true;
395407
}
396-
408+
logUtil.logClick(place.getBuilding().getName(), place.getName(), place.getFloor(), place.getType().toString());
397409
return new SearchPlaceDetailRes(place, imageUrl, bookmarked, placeImageList);
398410
}
399411

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package devkor.com.teamcback.global.logging.service;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import devkor.com.teamcback.domain.building.entity.Building;
5+
import devkor.com.teamcback.domain.place.entity.Place;
6+
import devkor.com.teamcback.domain.routes.entity.Conditions;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
import org.springframework.stereotype.Component;
10+
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
@Component
16+
public class LogUtil {
17+
18+
private static final Logger routeLog = LoggerFactory.getLogger("ROUTE_LOG");
19+
private static final Logger searchLog = LoggerFactory.getLogger("SEARCH_LOG");
20+
private static final Logger clickLog = LoggerFactory.getLogger("CLICK_LOG");
21+
22+
private final ObjectMapper objectMapper = new ObjectMapper();
23+
24+
public void logClick(String buildingName, String placeName, Double floor, String categoryName) {
25+
Map<String, Object> logMap = new HashMap<>();
26+
if(buildingName != null) logMap.put("buildingName", buildingName);
27+
if(placeName != null) logMap.put("placeName", placeName);
28+
if(floor != null) logMap.put("floor", floor);
29+
if(categoryName != null) logMap.put("categoryName", categoryName);
30+
31+
try {
32+
clickLog.info(objectMapper.writeValueAsString(logMap));
33+
} catch (Exception ignored) { }
34+
}
35+
36+
public void logSearch(String keyword) {
37+
Map<String, Object> logMap = new HashMap<>();
38+
logMap.put("keyword", keyword);
39+
40+
try {
41+
searchLog.info(objectMapper.writeValueAsString(logMap));
42+
} catch (Exception ignored) { }
43+
}
44+
45+
public void logRoute(Building startBuilding, Place startPlace, Building endBuilding, Place endPlace, List<Conditions> conditions) {
46+
Map<String, Object> logMap = new HashMap<>();
47+
48+
logMap.put("startBuildingName", (startBuilding != null) ? startBuilding.getName() : startPlace.getBuilding().getName());
49+
logMap.put("endBuildingName", (endBuilding != null) ? endBuilding.getName() : endPlace.getBuilding().getName());
50+
51+
if (startPlace != null) logMap.put("startPlaceName", startPlace.getName());
52+
if (endPlace != null) logMap.put("endPlaceName", endPlace.getName());
53+
if (conditions != null) logMap.put("conditions", conditions.stream().map(Enum::name).toList());
54+
55+
try {
56+
routeLog.info(objectMapper.writeValueAsString(logMap));
57+
} catch (Exception ignored) { }
58+
}
59+
}

src/main/resources/application.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,15 @@ spring:
4646
mail.smtp.auth: true
4747
mail.smtp.starttls.enable: true
4848

49+
# elasticsearch:
50+
# uris: ${ELASTIC_URIS}
51+
# username: ${ELASTIC_USERNAME}
52+
# password: ${ELASTIC_PASSWORD}
4953

5054
logging:
5155
level:
5256
org.hibernate.SQL: debug
57+
# org.elasticsearch.client: TRACE
5358

5459
jwt:
5560
secret:
@@ -90,6 +95,7 @@ springdoc:
9095
path: /swagger-ui.html
9196
doc-expansion: none
9297
enabled: true
98+
display-request-duration: true
9399
paths-to-match:
94100
- /api/**
95101

0 commit comments

Comments
 (0)