'SNS'에 해당하는 글 4건

Tomcat Log - Slack Alarm

Server/AWS 2020. 10. 28. 19:00

톰캣에 특정 키워드가 포함된 에러로그가 발생됐을 때 슬랙으로 알림을 받기 위해 필요한 서비스들은 AWS 의 cloudwatch, sns, lambda 등 이다. 이 서비스들로 과정을 대충 설명하자면...


  • cloudwatch 에 톰캣 로그를 남길 수 있다. 그리고 특정 키워드들의 필터링이 가능하다. 필터링 됐을때 경보(알림)이 가능하다.
  • 경보 설정에서 SNS 로 알림을 전송할 수 있다.
  • SNS 에 알림이 오면 슬랙에 메시지를 전달하는 Lambda 함수를 실행하게 하면 끝.



EC2 로그를 cloudwatch 로...

지표 필터(Metric Filter) 생성

경보를 처리할 SNS(Simple Notification Service) 생성

경보(Alarms) 생성

슬랙 웹훅(WebHooks URL) 생성

람다(Lambda) 생성



1. EC2 로그를 cloudwatch 로...


EC2 의 특정 로그를 cloudwatch 로 전달하기 위해서는 awslogs 가 설치되어 있어야 한다.

아래는 centos7 에서의 설치 및 설정 예이다. (OS 별로 설치 방법은 다름.)


# yum install awslogs
No matches found
 
# curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O
 
# python ./awslogs-agent-setup.py --region ap-northeast-1
AWS Access Key ID [None]: ********************
AWS Secret Access Key [None]: ****************************************
 
Path of log file to upload: /var/log/tomcat8/api.log
Destination Log Group name: /project1/var/log/tomcat8/api.log
 
Timestamp format: 3. %Y-%m-%d %H:%M:%S (2008-09-08 11:52:54)
cs


curl 로 다운받아 setup 파일을 리전과 함께 실행하면 IAM 및 로그 관련 설정을 하게 된다.

AWS Key 는 CloudWatch Logs 에 대한 권한이 있는 IAM 정보를 적으면 되고, 

EC2 의 어떤 로그 파일을 cloudwatch 로그 그룹으로 생성할 것인지 를 적으면 되고,

로그 파일의 datetime_format 을 선택하면 된다.


위에 설정한 항목들은 /var/awslogs/etc/awslogs.conf 파일에서 수정 및 추가가 가능하다.


...
[/var/log/tomcat8/catalina.log]
datetime_format = %d-%b-%Y %H:%M:%S
file = /var/log/tomcat8/catalina.*.log
buffer_duration = 5000
log_stream_name = {instance_id}
initial_position = start_of_file
log_group_name = /project1/var/log/tomcat8/catalina.log
 
[/var/log/tomcat8/web.log]
datetime_format = %Y-%m-%d %H:%M:%S
file = /var/log/tomcat8/web.log
buffer_duration = 5000
log_stream_name = {instance_id}
initial_position = start_of_file
log_group_name = /project1/var/log/tomcat8/web.log
...
cs


설정을 마쳤으면 awslogs 데몬을 올리고 부팅시 시작되도록 설정한다.


# systemctl start awslogs
# chkconfig awslogs on
cs


여기까지 마쳤다면 cloudwatch log-group 에서 catalina.log 등의 로그를 확인할 수 있다.


설정 중 datetime_format 의 형식대로 로그의 라인을 구분하므로 이 부분을 정확하게 선택하지 않으면 cloudwatch 에서 로그를 볼 수 없을 것이다. 만약 awslogs 데몬이 시작된 후에도 cloudwatch 에 로그가 생성되지 않았다면 /var/log/awslogs.log 파일에서 에러 로그를 확인한다.



2. 지표 필터(Metric Filter) 생성


cloudwatch 특정 로그의 [지표 필터] 탭에서 지표 필터를 생성한다.


우리의 목적은 로그에서 에러 문구를 추출하는 것이므로 [패턴 필터링] 항목에는 필터링할 키워드를 나열한다. error 나 exception 등 원하는 것들을 나열하고 [패턴 테스트] 항목에서 테스트를 할 수 있다. (아래 예에서는 ERROR, Error, error 라는 키워드를 필터링 하였다.) 지표의 세부 정보까지 입력하면 지표 필터가 생성고, 이것은 cloudwatch 의 경보(Alarms) 에서 사용될 것이다.



3. 경보를 처리할 SNS(Simple Notification Service) 생성


SNS 메뉴의 토픽을 표준(standard) 및 default 설정으로 하나 생성한다.

이 토픽은 경보를 받아 람다로 전달될 것이다.



4. 경보(Alarms) 생성


다시 cloudwatch 의 경보(Alarms) 로 돌아와 경보를 생성한다. 이미 생성한 지표를 선택하고 1분안에 에러가 발생한다면, 이미 생성한 SNS 토픽으로 전달하도록 설정한다.



5. 슬랙 웹훅(WebHooks URL) 생성


특정 채널로 메시지를 전달 받기 위해서는 슬랙에서 별도의 WebHooks 를 생성하고 람다에 입력해 주어야 한다.

슬랙앱에서 [Administration]-[Manage Apps] 를 클릭하면 브라우저로 Manage 페이지가 열린다.

webhooks 이 없다면 상단 App 검색창에서 검색하여 [Incoming WebHooks] 를 install 하고 [Add to Slack] 을 클릭하여 WebHooks 를 생성한다.



Post to Cannel 항목에는 메시지를 전달받을 채널을 선택하면 되고 Webhook URL 은 람다에 입력해야 하므로 기록해 놓는다.




6. 람다(Lambda) 생성


SNS 에 알림이 발생하면 실행될 람다를 생성한다.

함수 생성 - Blueprints :  cloudwatch-alarm-to-slack-python  를 선택.

구독할(위에 생성한) SNS 를 선택하고 약간의 코드 수정, 환경 변수 추가만 하면 슬랙으로 알림을 보낼 수 있다.


import boto3
import json
import logging
import os
 
from base64 import b64decode
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
 
 
# The base-64 encoded, encrypted key (CiphertextBlob) stored in the kmsEncryptedHookUrl environment variable
#ENCRYPTED_HOOK_URL = os.environ['kmsEncryptedHookUrl']
# The Slack channel to send a message to stored in the slackChannel environment variable
#SLACK_CHANNEL = os.environ['slackChannel']
 
#HOOK_URL = "https://" + boto3.client('kms').decrypt(
#    CiphertextBlob=b64decode(ENCRYPTED_HOOK_URL),
#    EncryptionContext={'LambdaFunctionName': os.environ['AWS_LAMBDA_FUNCTION_NAME']}
#)['Plaintext'].decode('utf-8')
 
HOOK_URL = os.environ['HOOK_URL']
 
logger = logging.getLogger()
logger.setLevel(logging.INFO)
 
 
def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))
 
    alarm_name = message['AlarmName']
    #old_state = message['OldStateValue']
    new_state = message['NewStateValue']
    reason = message['NewStateReason']
 
    slack_message = {
        'text'"%s state is now %s: %s" % (alarm_name, new_state, reason)
    }
 
    req = Request(HOOK_URL, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted")
        #logger.info("Message posted to %s", slack_message['channel'])
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)
cs


생성된 코드에서 기존 환경 변수인 kmsEncryptedHookUrl 과 slackChannel 을 사용하지 않고 HOOK_URL 을 직접 사용하는 코드로 변경한 것이다. 코드 수정 후 환경 변수 항목에 HOOK_URL 이름으로 WebHooks URL 을 입력하면 완성이다.


이제 톰캣 로그에 ERROR, Error, error 등의 키워드가 표시되면 슬랙으로 아래처럼 알림이 발송될 것이다.




로그 상세를 알람 메시지에 표시하고 싶었지만... 불가...




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

금단현상

Daily/Diary 2018. 12. 29. 00:22


금연 34일째... 자리 한번 잡았다 하면 최대 10대까지도 가능한 줄담배의 귀재인데 난 참 금단현상 같은거 없다.

그걸 매일 봐오던 동료들은 이렇게 갑자구 툭 담배에 관심을 끊은걸 보고 정말 신기해 한다. 내가 생각해도 희한하긴 하다.


  • 18년전 군대에서 몸살로 심하게 앓다가 이틀동안 담배를 못피운걸 계기로 9개월 금연.
  • 9년전 불꽃연애중 1년 금연.
  • 4년전 빼빼로데이 기념(?)이라며 갑자기 9개월 금연.
  • 얼마전 건강검진을 핑계로 시작된 마지막 금연.


자잘한 금연들을 빼고 그나마 성공적이었던건 저 셋 뿐이다.

이번에도 사실 건강검진 2주전부터 끊으려고 했었는데 실패하다 실패하다 5일전에 성공했다.

나도 그러했지만 금연할 때는 계기가 있으면 확실히 효과가 있긴 하다. 이것저것 금연할 핑계를 만드는거지.

예전에 금연할 때는 언젠간 다시 펴야지 못다핀 담배... 라는 생각을 했었는데, 이제는 그냥 다 지긋지긋하다. 정말 마지막 금연이 될 것 같은...


굳이 찾아내려고 한다면, 금단현상으로 볼 만한 짓을 하나 시작하긴 했다. SNS... ㅋㅋ

원래 남들 다 할 때 안하다가 뒷북치는게 특기기는 하지만 이번에도 참 쌩뚱맞다.

하지만 내가 하는 SNS 는 Social Network Service 가 아니다. 다른 사람들을 팔로우하고 공유하려는 목적이 아니다.

도구만 이용할 뿐 최소한의 관계망에서 최대한 흔적을 남겨 놓으려는 독거노인의 외로운 절규라고나 할까.


명분 없는 내 자신과의 싸움은 이제 유통기한이 지났다. 일례로 금연 중인 한 달간 운동을 반 넘게 빠졌다.

일찍 일어날 명분은 운동이 아닌 모닝담배인 것으로 결론났다. 그 모닝담배를 대체해 줄 수 있는게 바로 SNS 이다.

한 6개월째 조깅만 하고 있는데... SNS 에 누드 올릴 계획을 세운다면 분명 다시 열심히 운동을 할 것이다.

노력하는 모습이든 사치하는 모습이든 누군가에게 보여주려고 할 때 열정은 배가 된다. 특히 나란 놈은... 확실치는 않지만 지금은 그런것 같다. ㅋㅋ

누군가 옆에서 바로 잡아줄 사람이 없으니, 멍 때리거나 편하게만 살려는 마음이 강하게 드는 독거노인 종족에게 꼭 필요한 아이템이 아닌가 싶다. 




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

SNS crawling

Tool/OAuth 2017. 12. 19. 00:29

대부분의 SNS 마다 로그인/글쓰기/글읽기 등을 위한 API 를 제공한다. 또한 각 SNS 에서 제공하는 여러 토큰 유형을 각 서비스에 맞게 활용하여야 한다. 흔히 게임이나 웹서비스에서 이미 가입된 SNS 계정으로 로그인하는 방식이 일반적인 SNS 사용자 인증 방식이다. 각 사용자를 로그인 시켜, 해당 사용자의 정보를 가져오는 것이다. 예를 들어, A 라는 서비스를 사용시 페이스북 계정으로 로그인하면, 페이스북으로부터 로그인한(인증된) 사용자의 정보가 A 서비스로 전달되는, OAuth 라는 인증 방식을 주로 사용한다. 즉, 사용자의 데이터(개인 정보 등) 를 읽어올 때, 사용자 액세스 토큰을 생성하면 된다.


그렇다면 내 사이트에 뿔뿔히 흩어진 SNS 의 내 게시물들을 표시하고 싶다면 어떻게 해야 할까. 위 사용자 액세스 토큰을 취득하기 위해, 매번 각각의 SNS 로그인 화면에서 id/pw 를 입력할 수는 없다. 이 때는 각 SNS 의 개발자 모드에서 앱을 생성하고 발급된 client_id / client_secret 를 전달하는 식으로 앱 액세스 토큰을 발급받아 사용하면 된다. 아래는 SNS crawling 을 위해 로그인을 하지 않고 각 계정의 데이터를 가져온 Java 코드의 예이다. (without redirect_url)



Facebook


서비스가 Spring 기반이라 Spring Social Facebook 을 사용해 보려 했으나, 사용자 인증 방식 밖에 안된다. 즉, 로그인 인증을 거치게 된다. 페이스북 API 버전도 너무 오래된 걸 사용하고 있고... 다음처럼 페이스북 그래프 API 를 사용하여 앱 액세스 토큰을 발급받는다.


GET /oauth/access_token
    ?client_id={app-id}
    &client_secret={app-secret}
    &grant_type=client_credentials
Host: graph.facebook.com
cs


데이터를 읽어오거나 할 때는, 각 파라미터 끝에 access_token 만 추가하면 된다.


GET /v2.11/me/feed?access_token={access_token}
Host: graph.facebook.com
cs


반환된 데이터에서 paging.next 가 없어질 때까지 계속 돌리면 모든 피드 데이터를 가져올 수 있다.



Twitter


트위터는 조금 더 까다롭다. client_id / client_secret 를 전달하는데 Base64 인코딩을 하여 Authorization 헤더에 담아 POST 방식으로 보낸다. bearer access_token 이 반환되며, 이 토큰을 API 요청시 Authorization 헤더에 담아 요청하면 된다. 이 과정을 간단하게 해결해주는 라이브러리를 twitter4j 에서 제공한다.


gradle 등의 빌드 툴로 twitter4j 를 가져온다.


compile group: 'org.twitter4j', name: 'twitter4j-http2-support', version: '4.0.6'
cs


그리고 토큰을 가져와 twitter 인스턴스를 만드는 코드이다. consumerKey 와 consumerSecret 만 넣으면 된다.


ConfigurationBuilder cb = new ConfigurationBuilder();
 
cb.setApplicationOnlyAuthEnabled(true);
cb.setOAuthConsumerKey(consumerKey);
cb.setOAuthConsumerSecret(consumerSecret);
 
try {
    OAuth2Token token = new TwitterFactory(cb.build()).getInstance().getOAuth2Token();
 
    cb = new ConfigurationBuilder();
    cb.setApplicationOnlyAuthEnabled(true);
    cb.setOAuthConsumerKey(consumerKey);
    cb.setOAuthConsumerSecret(consumerSecret);
    cb.setOAuth2TokenType(token.getTokenType());
    cb.setOAuth2AccessToken(token.getAccessToken());
 
    twitter = new TwitterFactory(cb.build()).getInstance();
catch (TwitterException e) {
    e.printStackTrace();
}
cs


반환된 데이터에서 page + 1 로 반환되는 데이터가 없을 때까지 계속 돌리면 모든 트윗을 가져올 수 있다.



Youtube


유투브도 OAuth 2.0 기반의 인증 방식을 사용하지만 약간의 추가 설정이 필요하다. 우선 유투브 API 를 사용하려면 구글 개발자 콘솔에서 라이브러리의 YouTube Data API v3 을 활성화 해야 한다. 그리고 사용자 인증 정보 메뉴에서 서비스 계정(Service account) 키를 생성하고, 비공개 키가 포함된 정보를 json 파일로 다운받아야 한다. 서비스 계정은 사용자 정보에 액세스하지 않는 API를 호출하는 애플리케이션에서 사용된다.


표준 라이브러리를 사용하여 구현해 본다.


compile group: 'com.google.apis', name: 'google-api-services-youtube', version: 'v3-rev188-1.23.0'
compile group: 'com.google.oauth-client', name: 'google-oauth-client-java6', version: '1.23.0'
compile group: 'com.google.oauth-client', name: 'google-oauth-client-jetty', version: '1.23.0'
cs


그리고 YouTube 인스턴스를 만들어 데이터를 가져온다.


public static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
public static final JsonFactory JSON_FACTORY = new JacksonFactory();
 
private YouTube youtube;
 
public YoutubeService() {
    List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/youtube.readonly");
 
    GoogleCredential credential = null;
    try {
        credential = GoogleCredential.fromStream(YoutubeService.class
                .getResourceAsStream("/service-account.json")).createScoped(scopes);
    } catch (IOException e) {
        e.printStackTrace();
    }
 
    // This object is used to make YouTube Data API requests.
    youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
            .setApplicationName("oops4u").build();
}
cs


반환된 데이터에서 nextPageToken 이 없어질 때까지 계속 돌리면 모든 동영상 데이터를 가져올 수 있다.



Instagram


인스타그램은... 현재 인증 및 권한 부여를 위해 OAuth 2.0 을 사용중이나, 권한 부여 과정에서 return URL 을 필요로 하고 있으며 다른 대안은 없다. 그러나... 이걸 또 해낸 사람들이 있네.


https://github.com/postaddictme/instagram-java-scraper

https://jitpack.io/#postaddictme/instagram-java-scraper/0.3.0


인증이 필요없는 장점. 옛날 endpoint 를 가지고 있긴 한데 막힐 때까지는 쓸만할 듯.


OkHttpClient okHttpClient = new OkHttpClient();
Instagram instagram = new Instagram(okHttpClient);
cs


계정 게시물 수 가져오고 그만큼 미디어 겟하면 끝.



-----------------------------------------------------------------------------------


2018년 10월 - instagram 정책변경으로 위 api 막힘 - instagram4j 대체.


// https://mvnrepository.com/artifact/org.brunocvcunha.instagram4j/instagram4j
compile ('org.brunocvcunha.instagram4j:instagram4j:1.7') {
    exclude group: 'org.bytedeco'
}
cs



WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret

Facebook ID

Daily/Prog 2015. 9. 9. 01:10

 

 

 

Facebook API 를 사용하여 Token 을 얻어 로그인한 사용자의 정보를 얻어낼 수 있다.

 

https://www.facebook.com/dialog/oauth?client_id={facebook_AppID}&response_type=token&redirect_uri={RedirectUri}

 

위의 URL에 페이스북 개발자 페이지에서 등록한 App ID를 입력하고 토큰을 받을 redirect_uri 를 설정하면 토큰을 얻을 수 있다.
얻어낸 토큰을 이용하여 아래와 같이 필요한 항목들을 나열하면 해당 정보들을 얻을 수 있다.

 

https://graph.facebook.com/me?fields=id,email,name,first_name,last_name,gender,locale,picture&access_token={token}

 

문제는 반환된 id 가 facebook 에서 사용하는 유일한 아이디일 것으로 확신했던 것이 나의 큰 오산이었다.
Facebook API 버전을 v2.0 에서 v2.4 로 업그레이드를 한답시고 app_id 를 신규 발급 했는데 각자 다른 id 가 돌아오는게 아닌가.
기존 회원정보 DB랑 ID가 일치하지 않는다면...ㅋ;
Dash board 에서 세팅을 바꿔봐도 원래 id 는 돌아오지 않았고 API 버전 역시 다운그레이드 되지 않았다.
API 버전문제라 생각하고 구글링만 죽어라 하고...
결국 오랜 삽질 끝에 반환된 id는 Facebook 의 유일한 id가 아닌 등록된 App 의 유일한 id로 검증되었다.

 

https://www.facebook.com/app_scoped_user_id/861203280607126
https://www.facebook.com/app_scoped_user_id/951486641578789

 

두개의 App 에서 반환된 각자 다른 내 ID가, 모두 내 타임라인으로 들어가는 것을 확인.
ㅋㅋ...

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

트랙백  0 , 댓글  0개가 달렸습니다.
secret