Giter Site home page Giter Site logo

zcl_log_util's Introduction

ABAP Logging Class ZCL_LOG_UTIL

An another ABAP logging class allowing programs to focus on their functionality rather than being buried under lines of logging code.

Link to download latest versions :

Summary

Features Overview

  • Logging messages in own internal table type :
    • Next to statement MESSAGES.
    • System message stored in global structure SY.
    • Standard BAPI return structure or table (eg BAPIRET2).
    • Custom return structure or table.
    • A free message text.
    • A message using a message class.
  • Logging messages in Application Log (TCODE : SLG1) .
  • Displaying logs in the report :
    • In an ALV grid from your own log table.
      • The method can be used to display any kind of internal table.
    • In the same presentation of SLG1 from Application Log.
  • Managing logs between foreground and background execution (batch) :
    • For batch mode, you can easily set which message type must be displayed in the spool and in the protocol in an independent way.
  • The best for the end, overloading logs messages using provided settings table or your own one :
    • Update messages components on the fly according to the rules in settings table.
    • Skip messages.
    • Appending an extra message.
    • Messages can be uniquely identified by using a Spot ID in your program.

Introduction : Genesis of this class

As part of the development of ABAP interfaces program executed by batch, we have been confronted several times with subjects around error logs. First, we had problems with their display between the area of ​​the job execution protocol and those to display in the spool to send an email. Later we had a request to handle standard BAPI error messages being significant as false positive. Finally for an advanced follow-up for possible anomaly analysis in production, the use of the application can be a precious help.

All of its subjects are at the very close ABAP level (message management) but the implementation varies greatly depending on the nature of the subject. The reuse of codes is very complex because of its processive implementation.

The need to develop a class designed to cover all needs, without modifying the way of logging into the program and leaving the core of the program readable has become evident. It is for this reason that I decided to develop the ZCL_LOG_UTIL class. It is intended to be easy to use (minimum configuration) while offering a range of functions (requires more configuration, but always wants to be as simple as possible). Due to this complexity, please find detailed documentation of the class and its use.

Installation

  1. Install this project via ABAPGit.
  2. Create Application Log object in transaction code SLG0 :
    • Object : ZLOGUTIL (Main default object for entries registred with ZCL_LOG_UTIL).
  3. Run the report : ZCL_LOG_UTIL_INSTALL to fill the default table ZLOG_UTIL_OVERLO of the class for example report.
  4. The table maintenance view must be created manually thanks to transaction SE11 (See: Table Maintenance Generator)

Now you're ready to get started.

Getting Start

The ZCL_LOG_UTIL project comes with an example program that contains and uses all of the functionality offered by the class. Its goal is to allow all users to have an example of a precise use, method call or implementation of a feature for their projects. There is nothing worse than having the method without understanding how to use it.

First follow the guide to understand its use in its simplest form before using the ZCL_LOG_UTIL_EXAMPLES (SE38 / SE80) example program. I will mention which demo contain the appropriate code explain in this guide.

Initialization

For the simplest possible use, I advise you to use the type of log table provided with the class. Instantiation works in the same way as that of an ALV grid of class CL_SALV_TABLE :

" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table ,
      lr_log_util  TYPE REF TO   zcl_log_util               .

" Instanciation
zcl_log_util=>factory(
    IMPORTING
        e_log_util  = lr_log_util
    CHANGING
        c_log_table = lt_log_table
).

Available in Demo 010.

Logging & Display

To log messages, there are a number of different ways to proceed. I advise you to do it in the following way. This method offers the advantage of being able to do the where-used on the message class and on the message number, a powerful feature of SAP.

" Creating a variable to catch generated message
DATA: lv_dummy TYPE string .

" Raising message :
"   - 1.) Message texte is available in lv_dummy.
"   - 2.) Log raised message
MESSAGE e504(vl) INTO lv_dummy.
lr_log_util->log( ).

" Display you log table
lr_log_util->display( ).

Hints : When you use statement MESSAGE, SAP automatically feed structure SY. Using lr_log_util->log( ) will log message using the message components available in structure SY. When a standard Function Module or BAPI implicitely stored errors in SY you can log system message using log( ) method without writing statement MESSAGE.

Available in Demo 010.

Detailed documentation

Definition of log message

A log message in SAP is an integral part of the system which is very well designed. In the most basic usage case, they are used to handle the program logic errors to inform (I), warn (W) or interrupt (E or A) the program.

Texts messages are stored in a class message (ID) which associates them to a number. The message text can have until four variables placeholders (&).

The class message also handle the internationalization.

The best practice is to use class message to manage your own custom messages. You can raised the message by specifying the type or generate a text message for a log registry (internal table for final display or any purpose).

Keep in mind the following message components with their roles :

  • TYPE : Defines the kind of the message :
    • A (Abort) which will interrupt the program.
    • E (Error) which will interrupt the program.
    • W (Warning) which will display a warning in status bar. It did not interrupt the rest of the program.
    • I (Information) which will display a information popup. It did not interrupt the rest of the program.
  • ID : specifies the class message which contain the text
  • NUMBER : This is the number of the message to get in the provided class message (ID)
  • MESSAGE VALUE 1 : The message text variable to put in placeholders (first & or &1).
  • MESSAGE VALUE 2 : The message text variable to put in placeholders (second & or &2).
  • MESSAGE VALUE 3 : The message text variable to put in placeholders (third & or &3).
  • MESSAGE VALUE 4 : The message text variable to put in placeholders (fourth & or &4).

Instantiation methods

There are two ways to instantiate the ZCL_LOG_UTIL class. The first method will be the most frequent method. The second method makes it possible to defer the association of the internal table to a later moment in the processing of the program. It also offers a way to change log tables at any time which allows you to use a single instance to manage different internal tables.

  • First method : classic way, same as chapter Getting Start
" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table ,
      lr_log_util  TYPE REF TO   zcl_log_util               .

" Instanciation (making link between instance and internal table)
zcl_log_util=>factory(
    IMPORTING
        e_log_util  = lr_log_util
    CHANGING
        c_log_table = lt_log_table
).

Available in Demo 010.

  • Second method : differed way, for multiple log table in one report.
# -----[ Instanciation ]---------------------------------------
" Data declaration :
DATA: lr_log_util  TYPE REF TO   zcl_log_util .

" Instanciation
zcl_log_util=>factory(
    IMPORTING
        e_log_util  = lr_log_util
).
# -----[ Linking log table ]-----------------------------------
" Data declaration :
DATA: lt_log_table TYPE TABLE OF zcl_log_util=>ty_log_table .

" Linking log table :
lr_log_util->set_log_table(
    CHANGING
        t_log_table = lt_log_table
).

Available in Demo 065.

Logging methods

The main objective of this class is to provide the maximum possibility of logging different message sources with different format types using only one method. Here are the ways to log messages using the instance reference lr_log_util of class zcl_log_util.

Logging system message

By definition, system message are stored in the global system structure SY. Calling the method log( ), without import parameter will add the system message to the linked log table :

" Log system message
lr_log_util->log( ).

Available in Demo 010.

Logging next to statement MESSAGE

As mentioned previously, the statement MESSAGE will update the global system structure. So to log the message in the log table simplify do as following :

" Done like this, the message will be redirected into lv_dummy as string.
MESSAGE i123(zmm01) WITH 'message' 'components' INTO DATA(lv_dummy).
lr_log_util->log( ).

Available in Demo 010.

From my point of view, this is the best way to register an entry in the log table, because the here before statement will respond to the where-used case.

Logging a structure

You can log any kind of structure as long as you have defined the field roles. The zcl_log_util class already knows the field roles of the SAP standard message structure (and table).

Please find below structure which are natively handle by zcl_log_util :

Name Msg. Text Msg. Type Msg. ID Msg. Number Msg. Val. 1 Msg. Val. 2 Msg. Val. 3 Msg. Val. 4
zcl_log_util=>ty_log_table MESSAGE TYPE ID NUMBER MSGV1 MSGV2 MSGV3 MSGV4
sy - MSGTY MSGID MSGNO MSGV1 MSGV2 MSGV3 MSGV4
prott - MSGTY MSGID MSGNO MSGV1 MSGV2 MSGV3 MSGV4
bapiret1 MESSAGE TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapiret2 MESSAGE TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapi_coru_return - TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapi_order_return - TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bdcmsgcoll - MSGTYP MSGID MSGNR MSGV1 MSGV2 MSGV3 MSGV4

See chapter Set your own definitions / Set a custom log table type or unregistred SAP standard type to register a unknown structure. Jump

DATA ls_bapiret2 TYPE bapiret2.

" Considering ls_bapiret2 is not initial
lr_log_util->log( ls_bapiret2 ).

Logging a table

You can log any kind of table as long as you have defined the field roles. The zcl_log_util class already knows the field roles of the SAP standard message structure (and table).

Please find below structure which are natively handle by zcl_log_util :

Name Msg. Text Msg. Type Msg. ID Msg. Number Msg. Val. 1 Msg. Val. 2 Msg. Val. 3 Msg. Val. 4
zcl_log_util=>ty_log_table MESSAGE TYPE ID NUMBER MSGV1 MSGV2 MSGV3 MSGV4
sy - MSGTY MSGID MSGNO MSGV1 MSGV2 MSGV3 MSGV4
prott - MSGTY MSGID MSGNO MSGV1 MSGV2 MSGV3 MSGV4
bapiret1 MESSAGE TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapiret2 MESSAGE TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapi_coru_return - TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bapi_order_return - TYPE ID NUMBER MESSAGE_V1 MESSAGE_V2 MESSAGE_V3 MESSAGE_V4
bdcmsgcoll - MSGTYP MSGID MSGNR MSGV1 MSGV2 MSGV3 MSGV4

See chapter Set your own definitions / Set a custom log table type or unregistred SAP standard type to register a unknown structure. Jump

DATA lt_bapiret2 TYPE TABLE OF bapiret2.

" Considering lt_bapiret2 is not initial
lr_log_util->log( lt_bapiret2 ).

Available in Demo 040.

Logging using message class

The class offer the possibility to register a new entry in your log table by passing message components directly in the import parameter of the method log( ).

Be aware that "where-used" will not be able to find the message

lr_log_util->log(
    IMPORTING
        i_log_msgid = 'zmm01'
        i_log_msgno = '123'
        i_log_msgty = 'I'
        i_log_msgv1 = 'Message'
        i_log_msgv2 = 'Component'
*        i_log_msgv3 = 'optional'
*        i_log_msgv4 = 'optional'
)

Logging a free text message

If you have a simple text or a variable which contains your text message you can register it in the log table.

Note : The class is not able to find back the message components, so in the log table you will only find the message in the message column.

lr_log_util->log( 'My free text message' ).

Quick loggers

Available in Demo 090.

Adding non message components to your log entries

Available in Demo 030, Demo 050, Demo 060.

Logging in the Application Log (SLG1)

The zcl_log_util class can be used as library to handle logging throught Application Log. Indeed, all registered messages from method log( ), can be stored in a Application Log ledger.

This is not the maim purpose of this class, so some settings steps must be perform to enable Application Log.

Configuration

At least, to create a Application Log register, we have to set the main object and eventually one of its sub-object.

By default, zcl_log_util use the default main object ZLOGUTIL which you have created in during Installation step thanks to SLG0.

So at least, you can simply enable the Application log, but I advise to use your own SLG objects.

To handle Application Log from zcl_log_util, you can define a local reference to simplify the code writing or make direct call from your instance.

Here we will use a local reference lr_slg to increase code readability :

DATA lr_slg TYPE REF TO zcl_log_util_slg.

lr_slg = lr_log_util->slg( ).

Available in Demo 070.

Set the main object

To set/change your own main object, simply use the method set_object( ) :

" Main object ZMYPO must be exist in SLG0.
lr_slg->set_object( 'ZMYPO' ).

Available in Demo 070.

Set the sub-object

To set/change the sub-object, simply use the method set_sub_object( ) :

" Sub-object 'PO_CHANGE' must be a sub-obecjt of 'ZMYPO'.
lr_slg->set_sub_object( 'PO_CHANGE' ).

Available in Demo 070.

Set the external number

The external number is optionnal. Its helps to reduce result when you search for log in SLG1. For instance, you can set the PO Number as external number :

lr_slg->set_external_number( '4500001189' ).

Available in Demo 070.

Set the log retention time

The retention delay is required for the garbage collector to delete old log. By default, the retention is set to 30 days.

lr_slg->set_retention( 60 ).

Available in Demo 070.

Enabling / Disabling

You can enable / disable at anytime the recording of log message in the Application Log.

Simply use method enable( ) to activate the functionality and disabled( ) to turn off recording.

" Turn on Application Log
lr_slg->enable(  ).
" Turn off Application Log
lr_slg->disable(  ).

Available in Demo 070 and 080.

Enabling / Disabling Application Log will not close the register. Register is saved at the end of the program.

Display Application Log

If you want to display the Application Log in your report to avoid using TCODE SLG1, simply call method display( ).

" Display the Application Log register
lr_slg->display(  ).

Available in Demo 070.

Result in your own log table :

Result in Application Log using display( ) :

Overloading Log Messages

This functionality is the most complex of the class and this is the need which lead me to create it.

This feature must be used with parsimony, because rewriting messages is not harmless. This a very powerful functionality which is configured thanks to a customizing table. So, once in production, issues can became more complex for analysis.

So the programs which use overloading must be well documented in functional and technical documentation (SFD / STD) and as possible in the program using comments.

Explanations about overloading feature (Theoretical Part)

The overloading consist to alter the log message which is registering with method log( ) (also with message( )) according to rules set in the customization table.

Alteration can be one of the following :

  • Change the error message
  • Skipping the error message
  • Append an extra message

For instance, in our case, an interface program calls a BAPI which returns an error message. However, our processing was successfully done/commit in SAP. The customer asked us to consider this error message as false positive (and some other ones).

For this message we only change the message type from E to W.

Another example that can explain overloading functionality is if you want to return a more speaking message for end users than those SAP which can be a little bit too technical.

All components which compose a log message (Cf Definition of log message) can be overloaded (one to many at once).

Set Overloading Rules with Modes

Overloading must be managed using a customizing table. The class ZCL_LOG_UTIL allows you to reuse your own customizing table, but if you do not have this kind of table, she is delivered with her own table ZLOG_UTIL_OVERLO. (See chapter Using your own custom setting table to set your own table).

At least, the customizing table must have four inputs fields and three outputs fields - marked as Obligatory - to be functional, but please find all configurable fields with their role. You can find the corresponding field of table ZLOG_UTIL_OVERLO between brackets.

  • Input fields :
    • Obligatory :
      • To set the Message ID of registering message to overload. [INPUT1]
      • To set the Message Number of registering message to overload. [INPUT2]
      • To set the Message Type of registering message to overload. [INPUT3]
      • To set the Spot ID. We will see this feature later. [INPUT4]
    • Optional :
      • For pre-filtering : (identifying you development subject)
        • One standing for development code. [CODE]
        • One standing for development domain. [DOMAINE]
        • One standing for kind of data. [DATA]
      • For extra parameters :
        • Extra parameter 1. [INPUT5]
        • Extra parameter 2. No set
  • Output fields :
    • Obligatory :
      • To set the new value of the Message ID. [OUTPUT1]
      • To set the new value of the Message Number. [OUTPUT2]
      • To set the new value of the Message Type. [OUTPUT3]
    • Optional :
      • To set the new value of the Message value 1. [OUTPUT4]
      • To set the new value of the Message value 2. [OUTPUT5]
      • To set the new value of the Message value 3. [OUTPUT6]
      • To set the new value of the Message value 4. [OUTPUT7]
      • To set the overloading mode. [OUTPUT8]

Default Customizing Table

Important : If the field standing for overloading mode is not defined all rules will be processed as mode O (Overloading).

The overloading allows you to restrict rules to a dedicated scope using pre-filters and extra-parameters.

When you instantiate a reference of zcl_log_util, rules are loaded on the first call of method log( ). It selects rules from customizing table using pre-filters and stores them in an internal table of the instance. Overloading only works with rules internally stored to prevent many queries. Pre-filters allows you to get overloading rules for you program.

Eventually, if you want to have variants of rule, you can use - by settings up at anytime - in your program until two extra parameters to fine the rule selection. It can be useful to manage rules in different language.

Please find below a chart showing how fields are used to fine the selection :

Rule selection diagram

Available in Demo 105.

Altering the message component(s) (Mode O)

This mode offer you to edit one to many component of the message.

Considering you are logging the following message e508(vl) and you have this rule in the customizing table :

INPUT1 INPUT2 INPUT3 INPUT4 OUTPUT1 OUTPUT2 OUTPUT3
VL 508 E W

With this rule the message will be logged / displayed as w508(vl)

Note : The overloading mode is not set (available) in the rule, so this is this mode which used.

Skipping the error message (Mode I)

This mode offer the possibility to mute message which respond to a rule set in customization table.

Note : The mode field must be defined

Considering you are logging the following message w010(02) and you have this rule in the customizing table :

INPUT1 INPUT2 INPUT3 INPUT4 OUTPUT1 OUTPUT2 OUTPUT3 OUTPUT8
02 010 W I

With this rule the message will be ignored (no entry in table / no display)

Appending an extra message (Mode A)

This mode offer the possibility to add an extra message to those responding to the rule.

Considering you are logging the following message e001(vn) and you have this rule in the customizing table :

INPUT1 INPUT2 INPUT3 INPUT4 OUTPUT1 OUTPUT2 OUTPUT3 OUTPUT8
VN 001 E 01 012 E A

With this rule you will have both error message :

  • e001(vn)
  • e012(01)

Note : This mode is most designed when logs are registered in log table instead of raising message even if that works as well.

Configuration (Technical Part)

Even if the overloading functionality seams to be complex and powerful, the configuration is very easy.

In the program, the minimal configuration to do is to enable the feature. The rest is handle in the customization table.

Enabling / Disabling

By default, overloading functionality is not enabled. Use the following statement to activate it.

Note : You can enable / disabled the feature at any time in the program depending of your need.

" Direct method
lr_log_util->overload( )->enable( ).

" Using intermediate reference
DATA: lr_log_util_overload TYPE REF TO zcl_log_util_overload
lr_log_util_overload = lr_log_util->overload( ).
lr_log_util_overload->enable( ).

To disable :

lr_log_util_overload->disable( ).

Available in Demo 100.

Set pre-filters

The purpose of pre-filters is to reduce / restrict rules to a specific scope (mainly a program).

Rule selection diagram

The idea is to set pre-filter in the INITIALIZATION event. Then overloading through method log( ) will only use rule responding to pre-filters.

To set filter, please use these methods. You can you them in an independent way depending of your need and your customizing table.

lr_log_util->overload( )->set_filter_devcode_value( 'ZCLLOGUTIL' ).
lr_log_util->overload( )->set_filter_domain_value( 'BC' ).
lr_log_util->overload( )->set_filter_data_value( 'OVER_LOG' ).

Available in Demo 140.

Using Spot ID

The Spot ID is a special and dedicated parameter to use a specific rule from your customizing table during overloading.

Imagine you use a BAPI W_DELIVERY_UPDATE_2 more than one time lead by specific managements rules. In one case the error message is a false positive (little modification) and in the other case, the error message is a real issue. You may want to have a overloading rule for both case.

Spot ID is design to identified a precise moment in your program. It's inspired from Enhancement Point.

You can set and change the Spot ID at any time in the program. You can easily enable or disable the Spot ID.

Considering the following entries in your customization table :

INPUT1 INPUT2 INPUT3 INPUT4 OUTPUT1 OUTPUT2 OUTPUT3 OUTPUT8
VN 001 E VN 001 W O
VN 001 E MGT1 I
VN 001 E MGT2 01 012 E O

Please find below how to use Spot ID and the resulting behavior.

  1. Logging without using Spot ID :
    lr_log_util->e( 
        i_log_msgid = 'VN'
        i_log_msgno = '001'
    ).
    " Output message -> w001(vn) (Table Line 1)
  2. Setting & enabling Spot ID
    " Set Spot ID and enable it
    lr_log_util->spot( 'MGT1' )->start( ).
    lr_log_util->e( 
        i_log_msgid = 'VN'
        i_log_msgno = '001'
    ).
    " Result is no message will be raised/display/log
    " because table line 2 indicating mode I (ignoring the message)
  3. Changing Spot ID
    " Spot is already enabled. Just change the Spot ID
    lr_log_util->spot( 'MGT2' ).
    lr_log_util->e( 
        i_log_msgid = 'VN'
        i_log_msgno = '001'
    ).
    " Result is two message : e001(vn) (Original one)
                              e012(01) (Appended message from table line 3)
                                       (Mode A = Append)
  4. Stopping Spot ID
    " Spot is already enabled. Just change the Spot ID
    lr_log_util->spot( )->stop( ).
    lr_log_util->e( 
        i_log_msgid = 'VN'
        i_log_msgno = '001'
    ).
    " Output message -> w001(vn) (Table Line 1)

Available in Demo 120 and Demo 105.

Using Extra parameters
Using your own custom setting table

Available in Demo 110.

Overloading an already filled log table

Available in Demo 130.

Managing batch mode outputs

Managing output for the spool

Available in Demo 150.

Managing output for the protocol

Available in Demo 160.

Set your own definitions

Set a custom log table type or unregistred SAP standard type

Available in Demo 020.

Set a custom setting table that storing overloading rules

Available in Demo 110.

Extra feature

Display the content of any kind of internal table

zcl_log_util's People

Contributors

neooblaster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

zcl_log_util's Issues

Current login language

Current login language 'EN' does not match main language 'FR'. Select 'Advanced' > 'Open in Main Language'

I get this message when I try to import your project. I don't have FR installed on the system.
Can you help me?
BTW I like your project, works great.
Sorry if this is not good way to ask for help, let me know if I have to remove this.

GIT - Issue on installation

Issue with :

  • FUGR : ZCL_LOG_UTIL
  • PROG : ZCL_LOG_UTIL_TEST_CASES
  • PROG : ZCL_LOG_UTIL_EXAMPLE
  • AVAS : fa163e8bc7911eeb8be0053b22a9c1b7 (cancel)

ABAP - Syntax Error (7.40)

Issue with ABAP 7.40 on old SAP System :

  • ZCL_LOG_UTIL_BATCH->_UPDATE
  • ZCL_LOG_UTIL_DEFINE->_GET_STRUCT_NAME
  • ZCL_LOG_UTIL_SPOT
  • ZCL_LOG_UTIL->OVERLOAD (returning + exporting + changing)

README - About Overloading Modes

Do not forget to speak about overloading mode with data element ZDT_LOG_UTIL_OVERLOADING_MODE with different value (range) and default behavior when value is empty or setting table field not defined

DOC - Tag to change to modify main language

<LANGUAGE>F</LANGUAGE>
<DDLANGUAGE>F</DDLANGUAGE>
<SPRSL>F</SPRSL>
<LANGU>F</LANGU>


.clas  ( <LANGU>F</LANGU> )
.doma. ( <DDLANGUAGE>F</DDLANGUAGE> )
       ( <DTELMASTER>F</DTELMASTER> )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.