Seize the day

POST : SDP for win32/base lib

클릭 통계 기능, 서버 로깅

프로그램을 개발하다보면, 설치, 삭제, 업데이트를 얼마나 했는지, 어떤 기능을 얼마나 사용했는지 통계 정보가 필요할 때가 있다. 사용자가 특정 기능을 많이 사용한다면 그 기능을 더 개선하는 방향으로 업데이트 계획을 세울 수도 있다. 사용자가 하루에 프로그램을 몇 번 실행하는지, 중요한 오류 상황에서 서버에 로깅을 어떻게 남기는지 알아보겠다. 


이때까지 서버의 정보를 가져오는 것은 버전체크기능 뿐이었는데, 이때 사용한 API는 URLDownloadToCacheFile 였다. 이 API로는 서버에 특정 정보를 보내는 기능이 어려운 HttpClient를 구현할 필요가 있다. 


HttpClient를 구현한 오픈소스는 너무나 많고, 나도 Wininet 기반으로 구현해 본 것이 있지만 여러가지 이유로 여기서는 http://www.codeproject.com/Articles/66625/A-Fully-Featured-Windows-HTTP-Wrapper-in-C

의 소스를 사용하겠다.  WinHttp API 기반으로으로 Wininet보다는 더 빠르고 안정적일 것으로 기대한다. 

그 외에도 boost asio 기반의 cpp-netlib, ATL에도 winsock기반으로 HttpClient 구현코드가 있다. 
http://cpp-netlib.org/0.10.1/reference/http_client.html
http://msdn.microsoft.com/ko-kr/library/x74c73yy(v=vs.80).aspx
그 외에도
http://casablanca.codeplex.com/wikipage?title=HTTP%20Client&referringTitle=Documentation
CUrl http://curl.haxx.se/ 
이런것 들이 있다. 



클릭 통계

클릭 통계는 이벤트가 발생할 때 마다 카운트 1씩 서버로 전송할 수도 있고, 레지스트리에 클릭 횟수를 저장해 두었다가 한 꺼번에 전송할 수도 있다. 두 가지 방식 다 구현해 볼 생각이다. 

초기화
dfx::ClickStat::Initialize(HKEY_CURRENT_USER,
L"Software\\EasyRegistry\\clickstat",
L"http://mdiwebma.com/easyregistry/clickstat.php?proj=ER",
L"http://mdiwebma.com/easyregistry/logging.php?proj=ER");

클릭 정보를 저장할 레지스트리 위치과, 서버에 클릭통계를 저장할 주소와 서버 로깅을 저장하 주소를 지정한다. 


사용

# define _CLICK(name) dfx::ClickStat(name).Click()


...


if( _GetInt(L"autoUpdate", 1) )

{

_CheckVersion(false);

_CLICK(L"opt.autoupdate_no");

}

else

{

_CLICK(L"opt.autoupdate_yes");

}


_CLICK(L"app.run");


// 통계전송

dfx::SendAllClickStatAsync();


_CLICK 매크로를 이용해서 레지스트리에 클릭 횟수를 저장한다SendAllClickStatAsync 는 레지스트리에서 모든 DWORD값을 읽어와서 서버로 POST 데이타로 전송한다. 쓰레드를 이용해서 비동기로 전송한다.


서버는 

mysql table

create table clickstat (

_id    integer not null auto_increment primary key,

proj   char(10),

date1  date,

name   char(30),

count  int

);


alter table clickstat add unique dailyUniqueNameKey (proj, date1, name);


--logging

create table logging (

_id   integer not null auto_increment primary key,

date1 date,

proj  char(10),

module   char(50),

version   char(50),

file  char(255),

line  integer,

tag  char(20),

msg  char(255),

text1 text);

   


clickstat.php

<?php

include "config.php";

$proj = $_GET['proj'];

if( $proj == '' )

{

    header('HTTP/1.1 400 Bad Request (proj)');

    exit;

}


if( $_SERVER['HTTP_CLICKSTAT'] != 'ItsClickStatistics')

{

    header('HTTP/1.1 400 Bad Request (clickstat)');

    exit;

}


$today = date("Y-m-d");


$db_conn =  mysql_connect($_dbhost, $_dbuser, $_dbpass);


if(mysql_select_db($_dbname, $db_conn) )

{

    foreach( $_POST as $name => $count )

    {

        $sql = "insert into clickstat (proj, date1, name, count) values ('$proj', '$today', '$name', $count) on duplicate key update count = count + $count";

        mysql_query($sql, $db_conn);

    }

    mysql_close($db_conn);

}

?>



table에 필드 3개를 묶어서 Unique로 설정하고  insert into ......on duplicat key update ...  를 이용하여 insert 실패시 업데이트가 이루어 지도록 쿼리를 작성한다.  쿼리를 이렇게 작성하면 시간 타이밍적으로 중복으로 요청이 쌓이는 것을 막을 수 있다. 



설치삭제 업데이트 통계

nsis 셋의 스크립트 코드 

ReadRegStr $R0 HKLM "${_APPREGKEY}" "InstallDir"

  ${If} $R0 == ""

    ExecWait '"$INSTDIR\${_APP_FILENAME}" /install'

  ${Else}

    ExecWait '"$INSTDIR\${_APP_FILENAME}" /update'

  ${EndIf}



#  언인스톨..


ExecWait '"$INSTDIR\${_APP_FILENAME}" /uninstall'


설치중에 InstallDir에 값이 있으면 이미 설치된 것으로 판단하여 /update 파리미터를 전송한다. 

메인 어플에서는 통계전송을 위한 파라미터가 있으면 통계전송만 하고 프로그램을 바로 종료한다. 

BOOL CEasyRegistryApp::InitInstance()

{

dfx::ClickStat::Initialize(HKEY_CURRENT_USER,

L"Software\\EasyRegistry\\clickstat",

L"http://mdiwebma.com/easyregistry/clickstat.php?proj=ER",

L"http://mdiwebma.com/easyregistry/logging.php?proj=ER");


// 설치 삭제 업데이트 통계 전송 //nsis 셋업에서 호출됨..

CCommandLine cmd;

if( cmd.Check(L"install") )

{

dfx::SendClickStat(L"app.install", 1);

return FALSE;

}

else if( cmd.Check(L"uninstall") )

{

dfx::SendClickStat(L"app.uninstall", 1);

return FALSE;

}

else if( cmd.Check(L"update") )

{

dfx::SendClickStat(L"app.update", 1);

return FALSE;

}


SendClickStat 함수는 동기적으로 서버에 클릭 통계를 전송한다.


서버 클릭 통계 확인

http://mdiwebma.com/easyregistry/clickstat_view.php?proj=ER

ER-2013-09-01 clickstat
app_run: 2 
app_update: 1 
mnu_checkversion: 1 
mnu_runregedit: 1 

opt_autoupdate_no: 2 



서버로깅

dfx::SendLog(L"Unittest", L"1.0", L"unittest.cpp", __LINE__, L"Info", L"Msg", L"Text");

중요한 에러나 예외상황에서 서버에 로기를 남기는 코드가 필요하다. 


http://mdiwebma.com/easyregistry/logging_view.php?proj=ER


ER-2013-09-01 logging
2013-08-30 [ Unittest ] 1.0 , unittest.cpp (286) Info : Msg
Text

2013-08-30 [ Unittest ] 1.0 , unittest.cpp (286) Info : Msg

Text



중대한 오류가 발생했다고 사용자게 메시지박스를 띄우는 것은 쉬우나 그 메시지 알림을 개발자에 기꺼이 알려주는 사용자는 극히 드물다.  당연히 예외가 발생하지 않아야 하는 코드에 예외 발생시 서버로깅 코드를 추가 해 두면 나중에 프로그램의 버그를 잡고 개선하는데 많은 도움이 된다. 







top

posted at

2013. 9. 1. 11:12


CONTENTS

Seize the day
BLOG main image
김대정의 앱 개발 노트와 사는 이야기
RSS 2.0Tattertools
공지
아카이브
최근 글 최근 댓글
카테고리 태그 구름사이트 링크